flutter Update list, setstate does not work - flutter

video:https://drive.google.com/file/d/1Nh49PxHeCak4YkaCHVec8hQCzgTN7Kr-/view?usp=sharing
The subcomponent clicks on the callback function, and the background color of the subcomponent cannot be changed by changing the value of list
I tried using deep copy and it still didn't work. How can I change the code to switch the background color?
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<bool> tabsActive = [true,false,false,false,false];
void changeTabs(int view) {
print("view:$view");
for(int i = 0;i < tabsActive.length;i++) {
tabsActive[i] = false;
}
setState(() {
tabsActive[view-1] = true;
});
print(tabsActive);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body:Center(
child: Row(
children: <Widget>[
GameTabs("tab1",1,tabsActive[0],changeTabs),
GameTabs("tab2",2,tabsActive[1],changeTabs),
GameTabs("tab3",3,tabsActive[2],changeTabs),
GameTabs("tab4",4,tabsActive[3],changeTabs),
GameTabs("tab5",5,tabsActive[4],changeTabs),
],
),
)
);
}
}
typedef CallIntFun = void Function(int);
class GameTabs extends StatefulWidget {
final String title;
final int view;
final bool active;
CallIntFun changeTabs;
GameTabs(this.title,this.view,this.active,this.changeTabs);
#override
_GameTabsState createState() => _GameTabsState(title,view,active,changeTabs);
}
class _GameTabsState extends State<GameTabs> with SingleTickerProviderStateMixin {
AnimationController _controller;
final String title;
final int view;
final bool active;
Color bgColor;
CallIntFun changeTabs;
_GameTabsState(this.title,this.view,this.active,this.changeTabs);
#override
Widget build(BuildContext context) {
if (active) {
return ActiveGameTabs(title);
}
return Expanded(
child: GestureDetector(
onTap: (){
changeTabs(view);
},
child: Container(
height: 56,
padding: EdgeInsets.fromLTRB(0, 6, 0, 0),
decoration: BoxDecoration(
color: Color.fromRGBO(235, 235, 235, 1),
border: Border.all(
color: Colors.green,
width: 3.0,
style: BorderStyle.none
),
),
child: Text(
title,
textAlign: TextAlign.center,
),
),
)
);
}
}
class ActiveGameTabs extends StatelessWidget {
final String title;
ActiveGameTabs(this.title);
#override
Widget build(BuildContext context) {
return Expanded(
child: Container(
height: 56,
padding: EdgeInsets.fromLTRB(0, 6, 0, 0),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.green,
width: 3.0,
style: BorderStyle.none
),
),
child: Text(
title,
textAlign: TextAlign.center,
),
)
);
}
}

You are passing the values from widget to state when the state is created for the first time. So it won't change when there is a change in any of those property (Because state won't gets created each time). You want to change your GameTab implementation like this:
class GameTabs extends StatefulWidget {
final String title;
final int view;
final bool active;
CallIntFun changeTabs;
GameTabs(this.title,this.view,this.active,this.changeTabs);
#override
_GameTabsState createState() => _GameTabsState();
}
class _GameTabsState extends State<GameTabs> with SingleTickerProviderStateMixin {
AnimationController _controller;
#override
Widget build(BuildContext context) {
if (widget.active) {
return ActiveGameTabs(widget.title);
}
return Expanded(
child: GestureDetector(
onTap: (){
widget.changeTabs(widget.view);
},
child: Container(
height: 56,
padding: EdgeInsets.fromLTRB(0, 6, 0, 0),
decoration: BoxDecoration(
color: Color.fromRGBO(235, 235, 235, 1),
border: Border.all(
color: Colors.green,
width: 3.0,
style: BorderStyle.none
),
),
child: Text(
widget.title,
textAlign: TextAlign.center,
),
),
)
);
}
}

Related

Flutter) I want to change screen in ModalBottomSheet... WHAT IS PROBLEM..?

I made an int variable tab1 and a function addTab to control the screen in modalBottomSheet, and I thought that when TextButton in HandAdd1() is pressed, a function addTab is executed, and changes the int variable tab1.
In MyApp(), as a result HandAdd2() widget is shown according to the list in MyApp(), but int variable tab1 doesn't change no matter how many times I press the TextButton in HandAdd1(), so the screen doesn't change, and I don't know what the problem is.
Can you help me ?
Here's the code:
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int tab1 = 0;
addTab (){
setState(() {
tab1++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton:FloatingActionButton.extended(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
padding:EdgeInsets.fromLTRB(20,30,20,20),
height:MediaQuery.of(context).size.height * 50,
margin: const EdgeInsets.only(
left: 0,
right: 0,
bottom: 0,
),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft:Radius.circular(25),
topRight: Radius.circular(25),
),
),
child: Scaffold(body:[HandAdd1(addTab:addTab) , HandAdd2()][tab1]),
);
},
backgroundColor: Colors.transparent,
);
);
}
}
and here's HandAdd1() WIdget. (HandAdd2() is almost similar to HandAdd1())
class HandAdd1 extends StatefulWidget {
const HandAdd1({Key? key, this.addTab}) : super(key: key);
final void addTab;
#override
HandAdd1State createState() => HandAdd1State();
}
class HandAdd1State extends State<HandAdd1> {
#override
Widget build(BuildContext context) {
return Column(
children:[
Align(alignment:Alignment.centerLeft, child:Padding(
padding:EdgeInsets.fromLTRB(0,20,0,0,),
child:Text('''title''' ,
),
),
),
Padding(
padding:EdgeInsets.fromLTRB(0,20,0,20,),
child:Container(),
),
InkWell(child: Container(
margin:EdgeInsets.fromLTRB(0,20,0,0,),
alignment:Alignment.center,
width: MediaQuery.of(context).size.width * 50,
height:60,
decoration: BoxDecoration(
borderRadius:BorderRadius.all(Radius.circular(10)),
color: Colors.pinkAccent,
),
child:Text('button' , style: TextStyle(color:Colors.white , fontWeight:FontWeight.w500 , fontSize: 20,))
),
onTap: (){
widget.addTab;
}
),
],
);
}
}
You need to pass addTab function to a addTab property as a VoidCallback.
In your HandAdd1 widget,
Change from final void addTab; to final VoidCallback addTab;
And from widget.addTab; to widget.addTab();

How to have an onTap gesture multiple levels below effect a top level String Variable?

I've created a stateful Widget as my main page with a String variable, textToDisplay.
import 'package:flutter/material.dart';
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
#override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
String textToDisplay = 'Choose option';
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
Text(
'$textToDisplay',
style: TextStyle(fontSize: 20),
),
SizedBox(
height: 20,
),
Choices(onTap: (){setState(() {
textToDisplay =
});},),
],
),
),
);
}
}
I have then created a stateless widget in another dart file called Choices().
class Choices extends StatelessWidget {
Choices({required this.onTap});
final VoidCallback? onTap;
#override
Widget build(BuildContext context) {
return Column(
children: [
SizedBox(
height: 20,
),
Buttons(text: 'Option A', onTap: onTap),
SizedBox(
height: 10,
),
Buttons(text: 'Option B', onTap: onTap),
],
);
}
}
and in this are 2 stateless widget buttons which have the ontap gesture.
class Buttons extends StatelessWidget {
Buttons({required this.text, required this.onTap});
final String text;
final VoidCallback? onTap;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Container(
color: Colors.green,
width: 100,
height: 40,
child: Text(
text,
),
),
);
}
}
I can pass the onTap gesture up the tree but what I need to do is when a button is pressed, it updates the variable, textToDisplay to display option A or Option B, depending on which button has been pressed.
I thought i could do this with a stateless widget (Choices()) because the data isn't chageing it is only being read
any help would be greatly appreciated.
One way of updating the top-level string is the create your own Callback using ValueChanged.
Here is a complete working example:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MainPage(),
),
),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
#override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
String textToDisplay = 'Choose option';
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
Text(
textToDisplay,
style: TextStyle(fontSize: 20),
),
SizedBox(
height: 20,
),
Choices(
onTap: (val) {
setState(() {
textToDisplay = val;
});
},
),
],
),
),
);
}
}
class Choices extends StatelessWidget {
Choices({required this.onTap});
final ValueChanged? onTap;
#override
Widget build(BuildContext context) {
return Column(
children: [
SizedBox(
height: 20,
),
Buttons(
text: 'Option A',
onTap: onTap,
),
SizedBox(
height: 10,
),
Buttons(text: 'Option B', onTap: onTap),
],
);
}
}
class Buttons extends StatelessWidget {
Buttons({required this.text, required this.onTap});
final String text;
final ValueChanged? onTap;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
if (onTap != null) {
onTap!(text);
}
},
child: Container(
color: Colors.green,
width: 100,
height: 40,
child: Text(
text,
),
),
);
}
}
you can use function with parameter
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
#override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
String textToDisplay = 'Choose option';
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
Text(
textToDisplay,
style: const TextStyle(fontSize: 20),
),
const SizedBox(
height: 20,
),
Choices(
onTap: (text) {
_chageText(text);
},
),
],
),
),
);
}
void _chageText(String text) {
setState(() {
textToDisplay = text;
});
}
}
class Choices extends StatelessWidget {
const Choices({super.key, required this.onTap});
final Function(String text) onTap;
#override
Widget build(BuildContext context) {
return Column(
children: [
const SizedBox(
height: 20,
),
Buttons(text: 'Option A', onTap: () => onTap('Option A')),
const SizedBox(
height: 10,
),
Buttons(text: 'Option B', onTap: () => onTap('Option B')),
],
);
}
}
class Buttons extends StatelessWidget {
const Buttons({super.key, required this.text, required this.onTap});
final String text;
final VoidCallback? onTap;
#override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Container(
color: Colors.green,
width: 100,
height: 40,
child: Text(
text,
),
),
);
}
}

When dynamically creating DropDownButton - it doesn't work

When creating a DropDownButton through a class, the dropdown list itself does not open. Tell me, what is my mistake?
The button is in the "inactive" state. That is, when I click on it, I do not have a list with data
Yes, I'm sure the data is there.
I would not create a class for the button, but I have about 10 of them, and in order not to have a lot of repetitive code, I need to use the class
I can provide button code -
class DropDownButtons extends StatelessWidget {
final dynamic valueDropDown;
final String hintDropDown;
final List<DropdownMenuItem<dynamic>>? itemsDropDown;
final void Function(dynamic)? saveState;
const DropDownButtons({
Key? key,
required this.valueDropDown,
required this.hintDropDown,
required this.itemsDropDown,
required this.saveState
}) : super (key: key);
#override
Widget build(BuildContext context) {
return Container(
width: 270,
margin: EdgeInsets.only(top: 7),
child: Align(
alignment: Alignment.center,
child: DropdownButtonFormField(
hint: Text('hintDropDown'),
value: valueDropDown,
icon: const Icon(Icons.keyboard_arrow_down),
items: itemsDropDown,
onChanged: saveState,
)
)
);
}
}
And also, the display code -
DropDownButtons(valueDropDown: LoginClass.countryValue, hintDropDown: 'Text', itemsDropDown: LoginClass.countryDropDown, saveState: saveStateCountry()),
And of course, saving the stat -
saveStateCountry() {
(dynamic newValue) {
setState(() {
LoginClass.countryValue = newValue!;
print(LoginClass.countryValue);
});
};
}
minimum reproductive code -
class CreatedApplications extends StatelessWidget {
const CreatedApplications({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
saveStateCountry() {
(dynamic newValue) {
setState(() {
LoginClass.countryValue = newValue!;
print(LoginClass.countryValue);
});
};
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: SingleChildScrollView(
padding: EdgeInsets.all(15),
child: Column(children: [
Column(
children: <Widget>[
Align(
alignment: Alignment.center,
child: FractionallySizedBox(
widthFactor: 1,
alignment: Alignment.centerLeft,
child: Text('${now}'))),
],
),
SizedBox(
height: 12,
),
// const DropDown(),
DropDownButtons(valueDropDown: LoginClass.countryValue, hintDropDown: 'Выберите страну', itemsDropDown: LoginClass.countryDropDown, saveState: saveStateCountry()),
],
),
),
]
)
)
);
}
}
class DropDownButtons extends StatelessWidget {
final dynamic valueDropDown;
final String hintDropDown;
final List<DropdownMenuItem<dynamic>>? itemsDropDown;
final Function saveState;
const DropDownButtons({
Key? key,
required this.valueDropDown,
required this.hintDropDown,
required this.itemsDropDown,
required this.saveState
}) : super (key: key);
#override
Widget build(BuildContext context) {
return Container(
width: 270,
margin: EdgeInsets.only(top: 7),
child: Align(
alignment: Alignment.center,
child: DropdownButtonFormField(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(
const Radius.circular(25.0),
),
),
fillColor: Colors.lightBlueAccent.withOpacity(0.2),
filled: true
),
hint: Text(hintDropDown),
value: valueDropDown,
icon: const Icon(Icons.keyboard_arrow_down),
items: itemsDropDown,
onChanged: (dynamic newValue) {
saveState;
}
)
)
);
}
}

how to call using rawchip widget

Basically, I have define a list of array but how to call the list on the label. can someone help me
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(
color: AppColor.white,
width: MediaQuery.of(context).size.width,
child: RawChip(
showCheckmark: false,
label: Text(widget.list,
style: TextStyle(
color: isSelected ? Colors.black : const Color.fromARGB(255, 45, 110, 162),
fontFamily: 'Poppins'
),),
backgroundColor: isSelected ? Colors.white : const Color.fromARGB(255, 200, 222, 255),
shape: const StadiumBorder(
side: BorderSide(color: Colors.transparent)),
//selectedColor: Colors.red,
selected: isSelected,
onPressed: (){
setState(() {
isSelected = isSelected ? false : true;
});
},
widget.variableName is used to refer top level widget(StatefulWidget ). To get data within state class, just use local
class MyWidget extends StatefulWidget {
final int widgetData;
const MyWidget({
Key? key,
required this.widgetData,
}) : super(key: key);
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
final int stateData = 3;
late int makingLocal;
#override
void initState() {
super.initState();
makingLocal = widget.widgetData;
}
#override
Widget build(BuildContext context) {
...
}
}
in your case it will be just list but.
Container(
child: Column(children: list),
),

Adding a toggled button/icon to App in flutter

Looking for a way to implement a button that can be toggled back and forth between favorites and history. Is there a way to do this in flutter.
You can try using a custom widget like this one below:
toggle_button.dart
import 'package:flutter/material.dart';
class ToggleButton extends StatefulWidget {
final double width;
final double height;
final String leftDescription;
final String rightDescription;
final Color toggleColor;
final Color toggleBackgroundColor;
final Color toggleBorderColor;
final Color inactiveTextColor;
final Color activeTextColor;
final double _leftToggleAlign = -1;
final double _rightToggleAlign = 1;
final VoidCallback onLeftToggleActive;
final VoidCallback onRightToggleActive;
const ToggleButton(
{Key? key,
required this.width,
required this.height,
required this.toggleBackgroundColor,
required this.toggleBorderColor,
required this.toggleColor,
required this.activeTextColor,
required this.inactiveTextColor,
required this.leftDescription,
required this.rightDescription,
required this.onLeftToggleActive,
required this.onRightToggleActive})
: super(key: key);
#override
_ToggleButtonState createState() => _ToggleButtonState();
}
class _ToggleButtonState extends State<ToggleButton> {
double _toggleXAlign = -1;
late Color _leftDescriptionColor;
late Color _rightDescriptionColor;
#override
void initState() {
super.initState();
_leftDescriptionColor = widget.activeTextColor;
_rightDescriptionColor = widget.inactiveTextColor;
}
#override
Widget build(BuildContext context) {
return Container(
width: widget.width,
height: widget.height,
decoration: BoxDecoration(
color: widget.toggleBackgroundColor,
borderRadius: BorderRadius.all(
Radius.circular(50.0),
),
border: Border.all(color: widget.toggleBorderColor),
),
child: Stack(
children: [
AnimatedAlign(
alignment: Alignment(_toggleXAlign, 0),
duration: Duration(milliseconds: 300),
child: Container(
width: widget.width * 0.5,
height: widget.height,
decoration: BoxDecoration(
color: widget.toggleColor,
borderRadius: BorderRadius.all(
Radius.circular(50.0),
),
),
),
),
GestureDetector(
onTap: () {
setState(
() {
_toggleXAlign = widget._rightToggleAlign;
_leftDescriptionColor = widget.inactiveTextColor;
_rightDescriptionColor = widget.activeTextColor;
},
);
widget.onRightToggleActive();
},
child: Align(
alignment: Alignment(-1, 0),
child: Container(
width: widget.width * 0.5,
color: Colors.transparent,
alignment: Alignment.center,
child: Text(
widget.leftDescription,
style: TextStyle(
color: _leftDescriptionColor,
fontWeight: FontWeight.bold),
),
),
),
),
GestureDetector(
onTap: () {
setState(
() {
_toggleXAlign = widget._leftToggleAlign;
_leftDescriptionColor = widget.activeTextColor;
_rightDescriptionColor = widget.inactiveTextColor;
},
);
widget.onLeftToggleActive();
},
child: Align(
alignment: Alignment(1, 0),
child: Container(
width: widget.width * 0.5,
color: Colors.transparent,
alignment: Alignment.center,
child: Text(
widget.rightDescription,
style: TextStyle(
color: _rightDescriptionColor,
fontWeight: FontWeight.bold),
),
),
),
),
],
),
);
}
}
main.dart
import 'package:flutter/material.dart';
import 'package:stackovfl_70777885/toggle_button.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
padding: EdgeInsets.all(10.0),
child: ToggleButton(
width: 300.0,
height: 60.0,
toggleBackgroundColor: Colors.white,
toggleBorderColor: (Colors.grey[350])!,
toggleColor: (Colors.indigo[900])!,
activeTextColor: Colors.white,
inactiveTextColor: Colors.grey,
leftDescription: 'FAVORITES',
rightDescription: 'HISTORY',
onLeftToggleActive: () {
print('left toggle activated');
},
onRightToggleActive: () {
print('right toggle activated');
},
),
),
);
}
}
This should result in the following:
The onLeftToggleActive(): () {} and onRightToggleActive() {} in main are triggered depending on where the slider moves.