How to take value from dropdown and display as text on screen? - android

How to make Stylish drop down and get data from that drop down list and display it as text. How can I achieve this task? Here is my code in which I am making a flutter app that is a spin the bottle game. I have made a drop down in it but, how to get its value and print as a text on screen:
import 'package:flutter/material.dart';
import 'dart:math';
List<DropdownMenuItem<String>> listDrop = [];
loadData() {
listDrop = [];
listDrop.add(new DropdownMenuItem(
child: new Text('Item 1'),
value: "1",
));
listDrop.add(new DropdownMenuItem(
child: new Text('Item 2'),
value: "2",
));
listDrop.add(new DropdownMenuItem(
child: new Text('Item 3'),
value: "3",
));
}
class ImageRotate extends StatefulWidget {
#override
_ImageRotateState createState() => new _ImageRotateState();
}
class _ImageRotateState extends State<ImageRotate>
with SingleTickerProviderStateMixin {
AnimationController animationController;
static var rng = new Random();
double random_number = 0.0;
);
} }
new Container(
alignment: Alignment.centerRight,
padding: new EdgeInsets.only(top: 200.0, right: 100.0),
child: new DropdownButton(
style: new TextStyle(
color: Colors.redAccent,
fontWeight: FontWeight.bold,
),
items: listDrop,
hint: new Text(
"Select"
),
onChanged: loadData(),
),
),

you code is not clear and cut so i added full example for you.
import 'package:flutter/material.dart';
void main() => runApp(Home());
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home:MultipleDropDownPage()
);
}
}
class MultipleDropDownPage extends StatefulWidget {
MultipleDropDownPage({Key key}) : super(key: key);
#override
_MultipleDropDownPageState createState() => new _MultipleDropDownPageState();
}
class _MultipleDropDownPageState extends State<MultipleDropDownPage> {
String selectedValues;
#override
void initState() {
// TODO: implement initState
super.initState();
selectedValues = "1";
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: Text('Multi Drop'),
),
body: Column(
children: <Widget>[
new Text(selectedValues.toString()),
new DropdownButton<String>(
onChanged: (String value) {
setState(() {
selectedValues = value;
});
},
hint: new Text('Course Unit'),
value: selectedValues,
items: <String>["1", "2", "3", "4", "5"].map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList(),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
});
},
),
);
}
}

Related

How I use the value selected from drop down list from one file into the another file in flutter

I am working with flutter. I create a DropDown menu in the menu_list.dart file. I want to use the value selected from the user in (menu_list.dart file) in the add_screen.dart file. So, that I can upload it in the FireStore with other user information. The code is attached below.
I am glad if someone helps.
'add_Screen.dart'
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:database/widgets/menu_list.dart';
import 'package:flutter/material.dart';
class AddScreen extends StatelessWidget {
String? personname, personphone, vall;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Add Data"),
),
body: Column(
children: [
Container(
child: MenuList(),
),
SizedBox(
height: 15,
),
Container(
child: TextFormField(
onChanged: (String name) {
getStudentName(name);
},
decoration: InputDecoration(
labelText: "Name",
focusedBorder: OutlineInputBorder(),
),
),
),
SizedBox(
height: 15,
),
Container(
child: TextFormField(
onChanged: (String phone) {
getStudentPhone(phone);
},
decoration: InputDecoration(
labelText: "PhoneNumber",
focusedBorder: OutlineInputBorder(),
),
),
),
SizedBox(
height: 15,
),
Container(
child: RaisedButton(
child: Text("Add"),
onPressed: createData,
),
),
],
),
);
}
createData() {
Future<void> documentReference = FirebaseFirestore.instance
.collection("Students")
.doc("subcollection")
.collection("collectionPath")
.doc()
.set(
{
"PersonName": personname,
"PersonPhone": personphone,
},
);
}
getStudentName(name) {
this.personname = name;
}
getStudentPhone(phone) {
this.personphone = phone;
}
}
`
----------------------------------------------------------------------------------------------
'menu_list.dart'
`
import 'package:flutter/material.dart';
class MenuList extends StatefulWidget {
const MenuList({Key? key}) : super(key: key);
#override
_MenuListState createState() => _MenuListState();
}
class _MenuListState extends State<MenuList> {
final items = ['Maths', 'Urdu', 'English', 'Simple'];
String? value;
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: value,
isExpanded: true,
items: items.map(buildMenuItem).toList(),
onChanged: (value) => setState(
() => this.value = value,
),
);
}
}
DropdownMenuItem<String> buildMenuItem(String item) => DropdownMenuItem(
value: item,
child: Text(
item,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 30,
),
),
);
`
I think the best option for you, without overegineering the code, would be to pass a callback function that is received as parameter for the onChanged parameter of the DropdownButton.
It would look something like this:
In add_screen:
String _itemSelected;
MenuList(onChanged: (value) {
_itemSelected = value;
})
In menu_list:
class MenuList extends StatefulWidget {
final Function(String) onChanged;
const MenuList({required this.onChanged, Key? key}) : super(key: key);
#override
_MenuListState createState() => _MenuListState();
}
class _MenuListState extends State<MenuList> {
final items = ['Maths', 'Urdu', 'English', 'Simple'];
String? value;
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: value,
isExpanded: true,
items: items.map(buildMenuItem).toList(),
onChanged: widget.onChanged
);
}
}
I apologize if the code doesn't compile directly since I'm not able to compile it at the time.
Feel free to reach me if you have any questions
class AddScreen extends StatefulWidget {
#override
_AddScreenState createState() => _AddScreenState();
}
class _AddScreenState extends State<AddScreen> {
// make stateful
String? personname, personphone, vall, dropdownValue;
// define value for dropdownValue
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Add Data"),
),
body: Column(
children: [
Container(
child: MenuList(dropdownValue: dropdownValue),
// call MenuList as above passing dropdown value;
),
...
// other children
],
),
);
}
}
// menu_list.dart
import 'package:flutter/material.dart';
class MenuList extends StatefulWidget {
String dropdownValue;
// pass the value here
MenuList({Key? key, this.dropdownValue}) : super(key: key);
#override
_MenuListState createState() => _MenuListState();
}
class _MenuListState extends State<MenuList> {
final items = ['Maths', 'Urdu', 'English', 'Simple'];
// String? value;
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: widget.dropdownValue,
// set the value here
isExpanded: true,
items: items.map(buildMenuItem).toList(),
onChanged: (value) => setState(
() => widget.dropdownValue = value,
// set the value here
),
);
}
}

How To Update A Widget from Another Widget with A DropDown in Flutter and Dart?

I have two separate stateful widgets. One is a dropdown with values and one is a widget I would like to update onChange of the DropDown. My issue is that the second widget does not update whenever the dropdown is changed. Below is my full code.
Dropdown Widget
import 'package:flutter/material.dart';
class DropDownList extends StatefulWidget {
List<String> values;
String select;
DropDownList({
Key key,
this.values,
this.select,
}) : super(key: key);
#override
_DropDownListState createState() => _DropDownListState();
}
class _DropDownListState extends State<DropDownList> {
List<String> dropdownValues = [
"2021",
"2020",
"2019",
"2018",
];
var selectedValue = '2021';
#override
void initState() {
setState(() {
widget.values = dropdownValues;
widget.select = selectedValue;
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20.0),
child: DropdownButton<String>(
value: selectedValue,
icon: SizedBox.shrink(),
// iconSize: 24,
elevation: 0,
style: TextStyle(color: Colors.white),
selectedItemBuilder: (BuildContext context) {
return dropdownValues.map((String value) {
return Text(
value,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
);
}).toList();
},
underline: SizedBox.shrink(),
onChanged: (String newValue) {
setState(() {
selectedValue = newValue;
});
showJojo(selectedValue);
print(selectedValue);
},
items: widget.values.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
'${value}',
style: TextStyle(
color: Colors.deepPurple,
),
),
);
}).toList(),
),
);
}
static showJojo(String select) {
return Jojo(
select: select,
);
}
}
Second Widget To get the data
class _JojoState extends State<Jojo> {
String select;
_JojoState(this.select);
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Container(
child: new Text(
'${this.select}',
style: TextStyle(color: Colors.white),
),
);
}
}
Can someone point me in the right direction, please?
Amicably
Chris
You need to add didChangeDependencies() method here, here if that variable will change page will be compiled.
void didChangeDependencies() {
super.didChangeDependencies();
setState(() {
if (selectedValue != null) return;
});
}```

I need to go from first screen to second screen

I need to move from one screen to another. I made the transition from one class to another, but the main information of the second screen is indicated in the void main. How can I make the transition from one window to another without errors? So i need to transition from void main to void second. I'm sorry that my post has a lot of code, but help me please. Thanks in advance.
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
enum GenderList {male, female}
class MyForm extends StatefulWidget {
#override
State<StatefulWidget> createState() => Registr();
}
class Registr extends State {
final _formKey = GlobalKey<FormState>();
GenderList _gender;
bool _agreement = false;
Widget build(BuildContext context) {
return Container(padding: EdgeInsets.all(10.0), child: new Form(key: _formKey, child: new Column(children: <Widget>[
new Text('Имя пользователя:', style: TextStyle(fontSize: 20.0),),
new TextFormField(validator: (value){
if (value.isEmpty) return 'Пожалуйста введите свое имя';
}),
new SizedBox(height: 20.0),
new Text('Контактный E-mail:', style: TextStyle(fontSize: 20.0),),
new TextFormField(validator: (value){
if (value.isEmpty) return 'Пожалуйста введите свой Email';
String p = "[a-zA-Z0-9+.\_\%-+]{1,256}#[a-zA-Z0-9][a-zA-Z0-9-]{0,64}(.[a-zA-Z0-9][a-zA-Z0-9-]{0,25})+";
RegExp regExp = new RegExp(p);
if (regExp.hasMatch(value)) return null;
return 'Это не E-mail';
}),
new SizedBox(height: 20.0),
new Text('Ваш пол:', style: TextStyle(fontSize: 20.0),),
Row(children: [Expanded(child:RadioListTile(
title: const Text('Мужской'),
value: GenderList.male,
groupValue: _gender,
onChanged: (GenderList value) {setState(() { _gender = value;});},
),
),
Expanded(child: RadioListTile(title: const Text('Женский'),
value: GenderList.female,
groupValue: _gender,
onChanged: (GenderList value ){setState(() {_gender=value;
});}))
],
),
Column(children: <Widget>[(SizedBox(height: 95.0)),
Align(alignment:Alignment.bottomCenter ,
child: CheckboxListTile(
value: _agreement,
title: new Text('Я ознакомлен'+(_gender==null?'(а)':_gender==GenderList.male?'':'а')+' с документом "Согласие на обработку персональных данных" и даю согласие на обработку моих персональных данных в соответствии с требованиями "Федерального закона О персональных данных № 152-ФЗ".'),
onChanged: (bool value) => setState(() => _agreement = value),
),
),
SizedBox(height: 20.0),
RaisedButton(onPressed: (){
if(_formKey.currentState.validate()) {
Color color = Colors.red;
String text;
if (_gender == null) text = 'Выберите свой пол';
else if (_agreement == false) text = 'Необходимо принять условия соглашения';
else {text = 'Форма успешно заполнена'; color = Colors.green;}
Scaffold.of(context).showSnackBar(SnackBar(content: Text(text), backgroundColor: color,));
Navigator.push(context, MaterialPageRoute(builder: (context) => MainScreen()));
}
}, child: Text('Проверить'), color: Colors.blue, textColor: Colors.white,),],)
],)));
}
}
class NewsBoxFavourit extends StatefulWidget {
#override
State<StatefulWidget> createState() => MyFormState();
}
class MyFormState extends State {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
}
}
class NewsBox extends StatelessWidget {
String _imageurl;
NewsBox( {String imageurl,}) {
_imageurl = imageurl;
}
#override
Widget build(BuildContext context) {
if (_imageurl != null && _imageurl != '') return new Container(
color: Colors.black12,
height: 138.0,
child: Row(children: [
Image.network(_imageurl, width: 200.0, height: 150.0, fit: BoxFit.cover,),
Expanded(child: Container(padding: EdgeInsets.all(5.0), child: Column(children: [
Text('Дата'),
TextFormField(),
Text('Бег'),
TextFormField()
]
))
),
])
);
}
}
class NewsBoxFavourit2 extends StatefulWidget {
#override
State<StatefulWidget> createState() => MyFormState2();
}
class MyFormState2 extends State {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
}
}
class NewsBox2 extends StatelessWidget {
String _imageurl2;
NewsBox2( {String imageurl2,}) {
_imageurl2 = imageurl2;
}
#override
Widget build(BuildContext context) {
Align(alignment: Alignment.topCenter,);
if (_imageurl2 != null && _imageurl2 != '') return new Container(
color: Colors.black12,
height: 200.0,
child: Row(children: [
Image.network(_imageurl2, width: 200.0, height: 150.0, fit: BoxFit.cover,),
Expanded(child: Container(padding: EdgeInsets.all(5.0), child: Column(children: [
Text('Дата'),
TextFormField(),
Text('Отжимания'),
TextFormField()
]
))
),
])
);
}
}
class NewsBoxFavourit3 extends StatefulWidget {
#override
State<StatefulWidget> createState() => MyFormState3();
}
class MyFormState3 extends State {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
}
}
class NewsBox3 extends StatelessWidget {
String _imageurl3;
NewsBox3( {String imageurl3,}) {
_imageurl3 = imageurl3;
}
#override
Widget build(BuildContext context) {
if (_imageurl3 != null && _imageurl3 != '') return new Container(
color: Colors.black12,
height: 200.0,
child: Row(children: [
Image.network(_imageurl3, width: 200.0, height: 150.0, fit: BoxFit.cover,),
Expanded(child: Container(padding: EdgeInsets.all(5.0), child: Column(children: [
Text('Дата'),
TextFormField(),
Text('Пресс'),
TextFormField()
]
))
),
])
);
}
}
class NewsBoxFavourit4 extends StatefulWidget {
#override
State<StatefulWidget> createState() => MyFormState4();
}
class MyFormState4 extends State {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
}
}
class NewsBox4 extends StatelessWidget {
String _imageurl4;
NewsBox4( {String imageurl4,}) {
_imageurl4 = imageurl4;
}
#override
Widget build(BuildContext context) {
if (_imageurl4 != null && _imageurl4 != '') return new Container(
color: Colors.black12,
height: 138.0,
child: Row(children: [
Image.network(_imageurl4, width: 200.0, height: 150.0, fit: BoxFit.cover,),
Expanded(child: Container(padding: EdgeInsets.all(5.0), child: Column(children: [
Text('Дата'),
TextFormField(),
Text('Приседания'),
TextFormField()
]
))
),
])
);
}
}
class MyFormState5 extends State {
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
}
}
class NewsBox5 extends StatelessWidget {
String _imageurl5;
NewsBox5( {String imageurl5,}) {
_imageurl5 = imageurl5;
}
#override
Widget build(BuildContext context) {
Alignment.bottomCenter;
if (_imageurl5 != null && _imageurl5 != '') return new Container(
color: Colors.black12,
height: 138.0,
child: Row(children: [
Image.network(_imageurl5, width: 200.0, height: 150.0, fit: BoxFit.cover,),
Expanded(child: Container(padding: EdgeInsets.all(30.0), child: Column(children: [
Text('Дата'),
TextFormField(),
Text('Прыжки'),
TextFormField()
]
))
),
])
);
}
}
class MainScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {return SizedBox(width: 100 , height: 80,
child:
RaisedButton(onPressed: (){
Navigator.push(context, MaterialPageRoute(builder: (context) => SecondScreen()));
}, child: Text('Сформировать результаты')))
;
}
}
class SecondScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {return SizedBox(width: 200 , height: 100,
child:
Scaffold(
appBar: AppBar(title: Text('Результаты')),
body: Center(child: RaisedButton(onPressed: (){
Navigator.pop(context);
}, child: Text('Назад'))),
));
}
}
void main() => runApp(MaterialApp(debugShowCheckedModeBanner: false,
home: Scaffold(appBar: PreferredSize(preferredSize: Size.fromHeight(30.0),
child: AppBar(centerTitle:true, title: Text('Форма ввода'),),),
body: MyForm(),),),);
void second() => runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(),
body:
ListView(children: [
NewsBox(
imageurl: 'https://img.rl0.ru/0e5dfa5a89802f6ae40eea1312ee89c0/c615x400i/https/news.rambler.ru/img/2019/01/25160023.223341.8124.jpeg'),
NewsBox2(imageurl2: 'https://avatars.mds.yandex.net/get-zen_doc/1873797/pub_5e3d7968f58c3b19c8d9605e_5e40115dc0c2cd2f40ec8403/scale_1200'),
NewsBox3(imageurl3: 'https://www.bodybuilding.com/images/2018/april/the-best-ab-workout-for-a-six-pack-header.jpg'),
NewsBox4(imageurl4: 'http://vlzsport.ru/wp-content/uploads/prisedanie.png'),
NewsBox4(imageurl4: 'http://vlzsport.ru/wp-content/uploads/prisedanie.png'),
MainScreen()
],) ,
drawer: Drawer(child: ListView(
children: <Widget>[SizedBox(height: 0,),
Align(alignment: Alignment.bottomLeft,),
DrawerHeader(child: new Text("Меню"),
decoration: BoxDecoration (
color: Colors.blueAccent), ),
SizedBox(height: 10,),
Align(alignment: Alignment.topLeft,),
ListTile(
title: Text("Программа"),
trailing: Icon(Icons.arrow_forward_ios),
),
ListTile(
title: Text("Настройки"),
trailing: Icon(Icons.arrow_back_ios),
),
ListTile(title: Text('Результаты'),
trailing: Icon(Icons.arrow_back_ios,),
),
],
),),
)
)
);
You should have runApp() only once , to return a widget that will be the entry point for your app. After that you can go from one screen to another using Navigator.push() .
Please refer to this link that explains everything in detail :
https://flutter.dev/docs/cookbook/navigation/navigation-basics
I didn't actually went through your code because it is too bloated with irrelevant content. However, to pass data between one screen to another and vice-versa you just need to:
final bar = await Navigator.pushNamed(MyScreen2, arguments: foo);
Now on the MyScreen2 you can get your data on didChangeDependencies for example as such:
final data = ModalRoute.of(context).settings.arguments;
If you want to pass back data from MyScreen2 to the previous one when the route is popped, just do Navigator.pop(context, data); and that data will be assigned to the bar property above.

I'm trying to put a var in dropdownlist

This is my code
I want to put a var in dropdown list in flutter
new DropdownButton<String>(
// value: selectedPurpose,
hint: new Text(
'Select visitng purpose',
style: TextStyle(fontFamily: "Gotham"),
),
items: widget.size
.map((purposeTemp) {
return new DropdownMenuItem<String>(
value: purposeTemp,
child: new Text(
purposeTemp,
style: TextStyle(fontFamily: "Gotham"),
),
);
}).toList(),
onChanged: (String purpose) {
setState(() {
ss = purpose.toString();
});
},
// value: selectedPurpose,
)
am getting this error
type 'List<dynamic>' is not a subtype of type 'List<DropdownMenuItem<String>>'
what should I do to solve it
You can copy paste run full code below
You can use widget.size.map<DropdownMenuItem<String>>((String purposeTemp)
code snippet
items: widget.size.map<DropdownMenuItem<String>>((String purposeTemp) {
return DropdownMenuItem<String>(
value: purposeTemp,
child: Text(purposeTemp, style: TextStyle(fontFamily: "Gotham")),
);
}).toList(),
working demo
full code
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: Center(
child: MyStatefulWidget(
size: ['One', 'Two', 'Free', 'Four'],
),
),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
List<String> size;
MyStatefulWidget({Key key, this.size}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
String selectedPurpose;
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: selectedPurpose,
style: TextStyle(color: Colors.deepPurple),
hint: Text(
'Select visitng purpose',
style: TextStyle(fontFamily: "Gotham"),
),
onChanged: (String Value) {
setState(() {
selectedPurpose = Value;
});
},
items: widget.size.map<DropdownMenuItem<String>>((String purposeTemp) {
return DropdownMenuItem<String>(
value: purposeTemp,
child: Text(purposeTemp, style: TextStyle(fontFamily: "Gotham")),
);
}).toList(),
);
}
}

Dynamic children for TabView in flutter

I'm trying to build a Tabbed View that has lists as children.
Both the Category labels and the lists content will be fetched from a database.
I am passing the labels from the caller page and successfully passing them as a List.
Now I'm trying to load my lists, and I have built a Widget (myList) that returns successfully a Future ListView.
The problems are two:
Every time i swipe left or right, the list rebuilds itself, while I would like to have it built only once
How can I use the code I made to have the tabs' children actually reflect the labels and are loaded dinamically according to how many categories i have?
Right now my code is this:
import 'package:flutter/material.dart';
import 'package:flutter_app/ui/menu_category_list.dart';
// Each TabBarView contains a _Page and for each _Page there is a list
// of _CardData objects. Each _CardData object is displayed by a _CardItem.
List<Tab> Tabs(List<String> l){
List<Tab> list;
for (String c in l) {
list.add(new Tab(text: c));
}
return list;
}
class TabsDemo extends StatelessWidget {
const TabsDemo({ Key key , this.categorie}) : super(key: key);
final List<Tab> categorie;
#override
Widget build(BuildContext ctxt) {
return new MaterialApp(
title: "Nice app",
home: new DefaultTabController(
length: 5,
child: new Scaffold(
appBar: new AppBar(
title: new Text("Title"),
bottom: new TabBar(
tabs:
categories,
//new Tab(text: "First Tab"),
//new Tab(text: "Second Tab"),
),
),
body: new TabBarView(
children: [
new MenuCategoryList(),
new MenuCategoryList(),
new MenuCategoryList(),
new MenuCategoryList(),
new MenuCategoryList()
]
)
),
)
);
}
}
currently result
Thanks a lot in advance
You can use List<E>.generate to achieve this.
import 'package:flutter/material.dart';
Say you have a set of categories passed from your caller page. And let's say this is your list of categories.
List<String> categories = ["a", "b", "c", "d", "e", "f", "g", "h"];
Then you can do something like this to achieve what you desire.
class TabsDemo extends StatefulWidget {
#override
_TabsDemoState createState() => _TabsDemoState();
}
class _TabsDemoState extends State<TabsDemo> {
TabController _controller;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext ctxt) {
return new MaterialApp(
home: DefaultTabController(
length: categories.length,
child: new Scaffold(
appBar: new AppBar(
title: new Text("Title"),
bottom: new TabBar(
isScrollable: true,
tabs: List<Widget>.generate(categories.length, (int index){
print(categories[0]);
return new Tab(icon: Icon(Icons.directions_car), text: "some random text");
}),
),
),
body: new TabBarView(
children: List<Widget>.generate(categories.length, (int index){
print(categories[0]);
return new Text("again some random text");
}),
)
))
);
}
You can also set different set of widgets as the Tab's view. You can create a list of pages and follow the same method.
Absolutely true List<E>.generate best solution to solve.
Problems arise if you need to modify the arrays. They consist in the fact that when modifying an array you do not have the opportunity to use the same controller.
You can use the next custom widget for this case:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> data = ['Page 0', 'Page 1', 'Page 2'];
int initPosition = 1;
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: CustomTabView(
initPosition: initPosition,
itemCount: data.length,
tabBuilder: (context, index) => Tab(text: data[index]),
pageBuilder: (context, index) => Center(child: Text(data[index])),
onPositionChange: (index){
print('current position: $index');
initPosition = index;
},
onScroll: (position) => print('$position'),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
data.add('Page ${data.length}');
});
},
child: Icon(Icons.add),
),
);
}
}
/// Implementation
class CustomTabView extends StatefulWidget {
final int itemCount;
final IndexedWidgetBuilder tabBuilder;
final IndexedWidgetBuilder pageBuilder;
final Widget stub;
final ValueChanged<int> onPositionChange;
final ValueChanged<double> onScroll;
final int initPosition;
CustomTabView({
#required this.itemCount,
#required this.tabBuilder,
#required this.pageBuilder,
this.stub,
this.onPositionChange,
this.onScroll,
this.initPosition,
});
#override
_CustomTabsState createState() => _CustomTabsState();
}
class _CustomTabsState extends State<CustomTabView> with TickerProviderStateMixin {
TabController controller;
int _currentCount;
int _currentPosition;
#override
void initState() {
_currentPosition = widget.initPosition ?? 0;
controller = TabController(
length: widget.itemCount,
vsync: this,
initialIndex: _currentPosition,
);
controller.addListener(onPositionChange);
controller.animation.addListener(onScroll);
_currentCount = widget.itemCount;
super.initState();
}
#override
void didUpdateWidget(CustomTabView oldWidget) {
if (_currentCount != widget.itemCount) {
controller.animation.removeListener(onScroll);
controller.removeListener(onPositionChange);
controller.dispose();
if (widget.initPosition != null) {
_currentPosition = widget.initPosition;
}
if (_currentPosition > widget.itemCount - 1) {
_currentPosition = widget.itemCount - 1;
_currentPosition = _currentPosition < 0 ? 0 :
_currentPosition;
if (widget.onPositionChange is ValueChanged<int>) {
WidgetsBinding.instance.addPostFrameCallback((_){
if(mounted) {
widget.onPositionChange(_currentPosition);
}
});
}
}
_currentCount = widget.itemCount;
setState(() {
controller = TabController(
length: widget.itemCount,
vsync: this,
initialIndex: _currentPosition,
);
controller.addListener(onPositionChange);
controller.animation.addListener(onScroll);
});
} else if (widget.initPosition != null) {
controller.animateTo(widget.initPosition);
}
super.didUpdateWidget(oldWidget);
}
#override
void dispose() {
controller.animation.removeListener(onScroll);
controller.removeListener(onPositionChange);
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
if (widget.itemCount < 1) return widget.stub ?? Container();
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
alignment: Alignment.center,
child: TabBar(
isScrollable: true,
controller: controller,
labelColor: Theme.of(context).primaryColor,
unselectedLabelColor: Theme.of(context).hintColor,
indicator: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context).primaryColor,
width: 2,
),
),
),
tabs: List.generate(
widget.itemCount,
(index) => widget.tabBuilder(context, index),
),
),
),
Expanded(
child: TabBarView(
controller: controller,
children: List.generate(
widget.itemCount,
(index) => widget.pageBuilder(context, index),
),
),
),
],
);
}
onPositionChange() {
if (!controller.indexIsChanging) {
_currentPosition = controller.index;
if (widget.onPositionChange is ValueChanged<int>) {
widget.onPositionChange(_currentPosition);
}
}
}
onScroll() {
if (widget.onScroll is ValueChanged<double>) {
widget.onScroll(controller.animation.value);
}
}
}
You can use dynamic children using for loop within your Tabbarview Widget
List<String> categories = ["category 1" , "category 2", "category 3",];
return TabBarView(
children:[
for(var category in categories)
Text(category), // this widget will show a text with specific category. You can use any other widget
],
);
Null safety version
import 'package:flutter/material.dart';
class CustomTabView extends StatefulWidget {
final int? itemCount;
final IndexedWidgetBuilder? tabBuilder;
final IndexedWidgetBuilder? pageBuilder;
final Widget? stub;
final ValueChanged<int>? onPositionChange;
final ValueChanged<double>? onScroll;
final int? initPosition;
CustomTabView({this.itemCount, this.tabBuilder, this.pageBuilder, this.stub,
this.onPositionChange, this.onScroll, this.initPosition});
#override
_CustomTabsState createState() => _CustomTabsState();
}
class _CustomTabsState extends State<CustomTabView> with TickerProviderStateMixin {
late TabController controller;
late int _currentCount;
late int _currentPosition;
#override
void initState() {
_currentPosition = widget.initPosition!;
controller = TabController(
length: widget.itemCount!,
vsync: this,
initialIndex: _currentPosition,
);
controller.addListener(onPositionChange);
controller.animation!.addListener(onScroll);
_currentCount = widget.itemCount!;
super.initState();
}
#override
void didUpdateWidget(CustomTabView oldWidget) {
if (_currentCount != widget.itemCount) {
controller.animation!.removeListener(onScroll);
controller.removeListener(onPositionChange);
controller.dispose();
if (widget.initPosition != null) {
_currentPosition = widget.initPosition!;
}
if (_currentPosition > widget.itemCount! - 1) {
_currentPosition = widget.itemCount! - 1;
_currentPosition = _currentPosition < 0 ? 0 :
_currentPosition;
if (widget.onPositionChange is ValueChanged<int>) {
WidgetsBinding.instance!.addPostFrameCallback((_){
if(mounted) {
widget.onPositionChange!(_currentPosition);
}
});
}
}
_currentCount = widget.itemCount!;
setState(() {
controller = TabController(
length: widget.itemCount!,
vsync: this,
initialIndex: _currentPosition,
);
controller.addListener(onPositionChange);
controller.animation!.addListener(onScroll);
});
} else if (widget.initPosition != null) {
controller.animateTo(widget.initPosition!);
}
super.didUpdateWidget(oldWidget);
}
#override
void dispose() {
controller.animation!.removeListener(onScroll);
controller.removeListener(onPositionChange);
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
if (widget.itemCount! < 1) return widget.stub ?? Container();
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
alignment: Alignment.center,
child: TabBar(
isScrollable: true,
controller: controller,
labelColor: Theme.of(context).primaryColor,
unselectedLabelColor: Theme.of(context).hintColor,
indicator: BoxDecoration(
border: Border(
bottom: BorderSide(
color: Theme.of(context).primaryColor,
width: 2,
),
),
),
tabs: List.generate(
widget.itemCount!,
(index) => widget.tabBuilder!(context, index),
),
),
),
Expanded(
child: TabBarView(
controller: controller,
children: List.generate(
widget.itemCount!,
(index) => widget.pageBuilder!(context, index),
),
),
),
],
);
}
onPositionChange() {
if (!controller.indexIsChanging) {
_currentPosition = controller.index;
if (widget.onPositionChange is ValueChanged<int>) {
widget.onPositionChange!(_currentPosition);
}
}
}
onScroll() {
if (widget.onScroll is ValueChanged<double>) {
widget.onScroll!(controller.animation!.value);
}
}
}

Categories

Resources