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();
Related
I'm trying to find a way to click on an element in my navbar and have the page auto-scroll down to that section. My one-page website consists of a SingleChildScrollView with the different sections (e.g. about, services, contact us,..) as children of that scroll view. This structure is written in my HomeScreen class. The NavBar is in a different dart file and gets generated. How can I make it so that these get linked to each other. GIF: https://imgur.com/a/pukCfao
Homescreen class (gets initiated in main.dart)
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
TopSection(),
SizedBox(height: kDefaultPadding * 2),
AboutSection(),
ServiceSection(),
TestimonialSection(),
SizedBox(height: kDefaultPadding),
ContactSection(),
SizedBox(height: 50)
],
),
),
);
}
}
Part where you can click on element in nav bar
class _MenuState extends State<Menu> {
int selectedIndex = 0;
int hoverIndex = 0;
List<String> menuItems = [
"Home",
"About",
"Services",
"Testimonials",
"Contact"
];
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: kDefaultPadding * 2.5),
constraints: BoxConstraints(maxWidth: 1110),
height: 100,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
boxShadow: [kDefaultShadow],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(
menuItems.length,
(index) => buildMenuItem(index),
),
),
);
}
Widget buildMenuItem(int index) => InkWell(
onTap: () {
setState(() {
selectedIndex = index;
});
},
Example of navbar with underneath the sections
UPDATE:
I solved the problem with a little change in your code and work for me :)
I use ValueNotifier and ValueLisenableBuilder for transfer value & use ValueChanged for pass value to outside the menu widget.
but better and recommended using one state manager for this work .
for switch scroll can using GlobalKey and Scrollable.ensureVisible.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
#override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
late ValueNotifier<int> notifier;
final List<GlobalKey> itemKeys = [
GlobalKey(),
GlobalKey(),
GlobalKey(),
GlobalKey(),
GlobalKey()
];
#override
void initState() {
notifier = ValueNotifier(0);
super.initState();
}
#override
void dispose() {
notifier.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: ValueListenableBuilder<int>(
valueListenable: notifier,
builder: (context, val, widget) {
return Column(children: [
Menu(
valueChanged: (int value) {
print("$value");
notifier.value = value;
notifier.notifyListeners();
Scrollable.ensureVisible(itemKeys[value].currentContext!,
duration: Duration(seconds: 1),
// duration for scrolling time
alignment: .5,
// 0 mean, scroll to the top, 0.5 mean, half
curve: Curves.easeInOutCubic);
},
),
Item(title: "Home", key: itemKeys[0]),
Item(title: "About", key: itemKeys[1]),
Item(title: "Services", key: itemKeys[2]),
Item(title: "Testimonials", key: itemKeys[3]),
Item(title: "Contact", key: itemKeys[4]),
]);
},
)),
);
}
}
class Item extends StatelessWidget {
const Item({Key? key, required this.title}) : super(key: key);
final String title;
#override
Widget build(BuildContext context) {
return Container(
height: 400,
width: double.infinity,
color: Colors.deepPurpleAccent,
child: Text("$title"),
);
}
}
class Menu extends StatefulWidget {
Menu({Key? key, required this.valueChanged}) : super(key: key);
ValueChanged<int> valueChanged;
#override
State<Menu> createState() => _MenuState();
}
class _MenuState extends State<Menu> {
int selectedIndex = 0;
int hoverIndex = 0;
List<String> menuItems = [
"Home",
"About",
"Services",
"Testimonials",
"Contact"
];
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 70 * 2.5),
constraints: BoxConstraints(maxWidth: 1110),
height: 100,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(
menuItems.length,
(index) => Container(
color: selectedIndex == index ? Colors.red : Colors.green,
width: 100,
height: 100,
child: InkWell(
child: Text("${menuItems[index]}"),
onTap: () {
setState(() {
selectedIndex = index;
widget.valueChanged((index));
});
},
),
),
),
),
);
}
}
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;
}
)
)
);
}
}
I built custom app for training. I created buttons with gesture detector and i assigned number to them and i created global variable "score". I want buttons to add their numbers to "score" variable and i want to show the variable in a container but Somehow it does not work.Can it be about states?Does anyone help me?
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
appBarTheme: const AppBarTheme(
backgroundColor: Colors.deepPurple,
centerTitle: true,
elevation: 3,
),
),
home: const HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("My Application"),
),
body: const Body(),
);
}
}
int score = 5;
class Body extends StatefulWidget {
const Body({Key? key}) : super(key: key);
#override
State<Body> createState() => _BodyState();
}
class _BodyState extends State<Body> {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 50,
width: 200,
decoration: const BoxDecoration(color: Colors.grey),
child: Center(child: Text(score.toString())),
),
const SizedBox(
height: 70,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Numb(
color: Colors.pink,
numb: 1,
),
Numb(
color: Colors.pink,
numb: 2,
),
Numb(
color: Colors.pink,
numb: 3,
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Numb(
color: Colors.pink,
numb: 4,
),
Numb(
color: Colors.pink,
numb: 5,
),
Numb(
color: Colors.pink,
numb: 6,
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Numb(
color: Colors.pink,
numb: 7,
),
Numb(
color: Colors.pink,
numb: 8,
),
Numb(
color: Colors.pink,
numb: 9,
),
],
),
],
);
}
}
class Numb extends StatefulWidget {
final int? numb;
final Color? color;
const Numb({
Key? key,
required this.numb,
required this.color,
}) : super(key: key);
#override
State<Numb> createState() => _NumbState();
}
class _NumbState extends State<Numb> {
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
setState(() {
score += widget.numb!;
});
},
child: Container(
margin: projectPadding.allPad * 0.5,
decoration: BoxDecoration(color: widget.color),
height: MediaQuery.of(context).size.height * 0.06,
width: MediaQuery.of(context).size.width * 0.1,
child: Center(
child: Text(widget.numb.toString()),
),
),
);
}
}
class projectPadding {
static const EdgeInsets horizantalPad = EdgeInsets.symmetric(horizontal: 20);
static const EdgeInsets verticalPad = EdgeInsets.symmetric(vertical: 20);
static const EdgeInsets allPad = EdgeInsets.all(20);
}
Numb setState only update the _NumbState ui. in order to update parent widget, you can use callback method that will trigger setState on parent UI.
class Numb extends StatefulWidget {
final int? numb;
final Color? color;
final Function(int) callBack;
const Numb({
Key? key,
required this.numb,
required this.color,
required this.callBack,
}) : super(key: key);
#override
State<Numb> createState() => _NumbState();
}
///....
onTap: () {
setState(() {
score += widget.numb!;
});
widget.callBack(score);
},
And you can add logic and others operation like
Numb(
color: Colors.pink,
numb: 1,
callBack: (p0) {
setState(() {});
},
),
I will also recommend starting state-management like riverpod / bloc
Why doesn't it work?
When you use the setState method, it triggers a rebuild of the widget where it is called.
In your code, you call the setState method in your Numb widget, which will therefore be rebuilt.
The problem is that the score value that you display on screen is located in your Body widget, and you want this widget to be rebuilt whenever the score changes.
So how do we do that?
How to make it work?
This is a State Management issue and there are multiple ways to solve it. You can find in the official documentation a good example to understand how this works.
Lifting the State Up and Callbacks
Following the previous logic described above, you would have to call the setState method in the Body widget to trigger a rebuild, and this whenever the score changes, which means whenever a Numb widget is pressed.
For that you can take advantage of callbacks which are basically functions that you can pass as parameters, and that will run the code they contain when they are called (you can see the official documentation's example about accessing a state using callbacks).
class Numb extends StatelessWidget { //<-- you can turn Numb into a StatelessWidget which is much simpler since you don't have to call the 'setState' method in it
final int? numb;
final Color? color;
final Function(int) callback; //<-- add your callback as a field...
const Numb({
Key? key,
required this.numb,
required this.color,
required this.callback, //<-- ... and in the constructor...
}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => callback(numb!), //<-- ... and simply call it this way in the onTap parameter, giving it the numb value
child: Container(
margin: projectPadding.allPad * 0.5,
decoration: BoxDecoration(color: color),
height: MediaQuery.of(context).size.height * 0.06,
width: MediaQuery.of(context).size.width * 0.1,
child: Center(
child: Text(numb.toString()),
),
),
);
}
}
class _BodyState extends State<Body> {
#override
Widget build(BuildContext context) {
var updateScoreCallback = (int number) => setState(() => score += number); //<-- your callback takes an int as parameter and call setState adding the input number to the score
return Column(
children: [
...,
Numb(
color: Colors.pink,
numb: 1,
callback: updateScoreCallback, //<-- give your callback to your Numb widgets
),
Numb(
color: Colors.pink,
numb: 2,
callback: updateScoreCallback,
),
...
}
}
Like that, pressing any of your Numb widget will call the callback, that will call the setState method in the appropriate Body widget.
State Management with Packages
The previous method to handle state management with callbacks is good when your case is simple, but if you need for example to access a state from multiple places in your code, it can be quickly too difficult to manage and inefficient. For that, there are multiple packages that are available to make things easier. The official recommandation to start with is the Provider package as it is simple to use, but depending on your need you may want to look for other options like BLoC, Redux, GetX, etc. (full list of state management approaches.
Using the Provider approach, start by adding the package in your project (flutter pub add provider), and add the following changes to your code:
import 'package:provider/provider.dart';
class ScoreData with ChangeNotifier { //<-- create a structure to hold your data, and use the mixin 'ChangeNotifier' to make ScoreData able to notify its listeners for any changes
int _score = 5;
int get score => _score;
void addToScore(int number) {
_score += number;
notifyListeners(); //<-- this method comes from ChangeNotifier (which comes from the Flutter SDK and not from the Provider package), and notify all listeners
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("My Application"),
),
body: ChangeNotifierProvider<ScoreData>( //<-- this says that at this level of the widget tree, you provide a ScoreData that can notify its listeners for any changes...
create: (context) => ScoreData(), //<-- ... and you provide the ScoreData here by creating a new one (you can provide an existing one using the "value" parameter)
child: const Body(),
),
);
}
}
class Body extends StatefulWidget {
const Body({Key? key}) : super(key: key);
#override
State<Body> createState() => _BodyState();
}
class _BodyState extends State<Body> {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 50,
width: 200,
decoration: const BoxDecoration(color: Colors.grey),
child: Center(
child: Text(Provider.of<ScoreData>(context).score.toString())), //<-- this enables you to retrieve the provided ScoreData higher in the widget tree, and to listen to its value
),
...
],
);
}
}
class Numb extends StatelessWidget {
final int? numb;
final Color? color;
const Numb({
Key? key,
required this.numb,
required this.color,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () =>
Provider.of<ScoreData>(context, listen: false).addToScore(numb!), //<-- here as well you retrieve the provided ScoreData, but you only want to call its method "addToScore" without needing to listen to its changes, so add the "listen: false" parameter to make it work
child: Container(
...
),
);
}
}
I'am noob of flutter and i want a create some apps. This is my main screen with animated container.
import 'package:flutter/material.dart';
import "./loginmenu.dart";
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Color.fromRGBO(197, 197, 187, 1),
body: AnimatedContainer(
duration: Duration(milliseconds: 350),
width: double.infinity,
height: double.infinity,
child: Image(image: AssetImage("assets/images/Babapps-logos.jpeg")),
onEnd: () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => loginscreen()),
),
),
),
);
}
}
when animation duration finish i want go new screen.
import 'package:flutter/material.dart';
class loginscreen extends StatefulWidget {
const loginscreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Column(
children: <Widget>[
Container(width: double.infinity, margin: EdgeInsets.all(130)),
Container(
width: double.infinity,
margin: EdgeInsets.all(5),
child: Center(
child:
Text("Welcome Diet&Life", style: TextStyle(fontSize: 19)),
),
),
Container(
width: 320,
margin: EdgeInsets.all(5),
child: Center(
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(), hintText: "Username"),
),
),
),
Container(
width: 320,
margin: EdgeInsets.all(5),
child: Center(
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(), hintText: "Password"),
)),
),
Container(
child: Center(
child: RaisedButton(
child: Text("Login"),
onPressed: null,
),
),
),
Container(
margin: EdgeInsets.all(10),
child: Center(
child: new InkWell(
child: Text("Don't have an account?"), onTap: null),
),
)
],
),
),
);
}
#override
State<StatefulWidget> createState() {
// TODO: implement createState
throw UnimplementedError();
}
}
but when I run this code, the animation does not go to the other screen even though it expires. Am I on the right track or do I have to work with setstate?
There is nothing changing on Container, therefor animation onEnd never gets call.
You need to change something inside the container in order to animate it.
If you just like to navigate after some delay, Just use Future.delayed then navigate.
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return const MaterialApp(home: StartPage());
}
}
class StartPage extends StatefulWidget {
const StartPage({Key? key}) : super(key: key);
#override
State<StartPage> createState() => _StartPageState();
}
class _StartPageState extends State<StartPage> {
Color containerColor = Colors.cyanAccent;
#override
void initState() {
super.initState();
_nextPage();
}
/// just nevigate
_nextPage() async {
Future.delayed(Duration(seconds: 1)).then((value) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AnimE(),
),
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
containerColor = Colors.blue;
});
},
),
body: AnimatedContainer(
duration: const Duration(seconds: 2),
color: containerColor,
width: double.infinity,
height: double.infinity,
child: const Text("a"),
onEnd: () {
print("Ebnd"); // paste neviagte by removing init
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => loginscreen(),
// ),
// );
}),
);
}
}
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,
),
),
)
);
}
}