I'm making one demo for learning purpose. In that demo I have just added ListView and when I tap on the ListTile there is Icons.favorite I just want to do Active and Deactive.
Below are my code.
import 'package:flutter/material.dart';
void main() => runApp(new RandomWordApp());
class RandomWordApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return new MaterialApp(
title: "Random App",
home: new RandomWordHome(),
);
}
}
class RandomWordHome extends StatefulWidget {
#override
createState() => new RandomState();
}
class RandomState extends State<RandomWordHome> {
#override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text("Random Word"),
),
body: setupListView(),
);
}
Widget setupListView() {
final _arrayOfData = [
{
"name": "Amit Patel",
"address": "Junagadh"
},
{"name": "Arjun Jain", "address": "Madhya Pradesh"},
{"name": "Ajay Shah", "address": "Limbadi"},
{"name": "Ankur Patel", "address": "Visanagar"},
{"name": "Soheb Ansari", "address": "Ahmedabad"}
];
final _arraySelectedRowStatus = List.filled(5, 0);
return new ListView.builder(
padding: new EdgeInsets.all(10.0),
itemCount: _arrayOfData.length,
itemBuilder: (context, i) {
return new ListTile(
title: new Text(_arrayOfData[i]["name"]),
subtitle: new Text(_arrayOfData[i]["address"]),
leading: new CircleAvatar(
backgroundColor: Colors.blue,
child: new Text(_arrayOfData[i]["name"][0])),
trailing: new Icon(
(_arraySelectedRowStatus[i] == 0
? Icons.favorite_border
: Icons.favorite),
color: (_arraySelectedRowStatus[i] == 0 ? null : Colors.red),
),
onTap: () {
setState(
() {
_arraySelectedRowStatus[i] =
(_arraySelectedRowStatus[i] == 0 ? 1 : 0);
Scaffold.of(context).showSnackBar(new SnackBar(
content:
new Text("You are selecting at " + i.toString())));
},
);
},
);
});
}
}
All are working well, even I got expected output from below line
print(_arrayOfData[i]["name"] + " - " + _arraySelectedRowStatus[i].toString());
But Icon is not change might be because ListView is not reloading. Where I'm going wrong? Guide me on right direction.
There are some issues with your approach
In order to update your UI, you have to use setState() method. Changing the variable is not enough.
You always create a final List inside ListView.builder block: final _arraySelectedRowStatus = List.filled(5, 0); and then use this list to render your UI. So the _arraySelectedRowStatus will always contain 0.
Below is updated code which will work
import 'package:flutter/material.dart';
void main() => runApp(new RandomWordApp());
class RandomWordApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return new MaterialApp(
title: "Random App",
home: new RandomWordHome(),
);
}
}
class RandomWordHome extends StatefulWidget {
#override
createState() => new RandomState();
}
class RandomState extends State<RandomWordHome> {
List _arraySelectedRowStatus = List.filled(5, 0);
#override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text("Random Word"),
),
body: setupListView(),
);
}
Widget setupListView() {
final _arrayOfData = [
{"name": "Amit Patel", "address": "Junagadh"},
{"name": "Arjun Jain", "address": "Madhya Pradesh"},
{"name": "Ajay Shah", "address": "Limbadi"},
{"name": "Ankur Patel", "address": "Visanagar"},
{"name": "Soheb Ansari", "address": "Ahmedabad"}
];
return new ListView.builder(
padding: new EdgeInsets.all(10.0),
itemCount: _arrayOfData.length,
itemBuilder: (context, i) {
return new ListTile(
title: new Text(_arrayOfData[i]["name"]),
subtitle: new Text(_arrayOfData[i]["address"]),
leading: new CircleAvatar(
backgroundColor: Colors.blue,
child: new Text(_arrayOfData[i]["name"][0])),
trailing: new Icon(
(_arraySelectedRowStatus[i] == 0
? Icons.favorite_border
: Icons.favorite),
color: (_arraySelectedRowStatus[i] == 0 ? null : Colors.red),
),
onTap: () {
_arraySelectedRowStatus[i] =
(_arraySelectedRowStatus[i] == 0 ? 1 : 0);
print(_arrayOfData[i]["name"] +
" - " +
_arraySelectedRowStatus[i].toString());
setState(() {
_arraySelectedRowStatus = _arraySelectedRowStatus;
});
Scaffold.of(context).showSnackBar(new SnackBar(
content: new Text("You are selecting at " + i.toString())));
},
);
});
}
}
[Update]
Here is the complete working code.
class DemoHelp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
title: "Random App",
home: RandomWorldHome(),
);
}
}
class RandomWorldHome extends StatefulWidget {
#override
_RandomWorldHomeState createState() => new _RandomWorldHomeState();
}
class _RandomWorldHomeState extends State<RandomWorldHome> {
List<int> _arraySelectedRowStatus = List.filled(5, 1);
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("Random Word"),
),
body: setupListView(),
);
}
Widget setupListView() {
final _arrayOfData = [
{"name": "Amit Patel", "address": "Junagadh"},
{"name": "Arjun Jain", "address": "Madhya Pradesh"},
{"name": "Ajay Shah", "address": "Limbadi"},
{"name": "Ankur Patel", "address": "Visanagar"},
{"name": "Soheb Ansari", "address": "Ahmedabad"}
];
return ListView.builder(
padding: new EdgeInsets.all(10.0),
itemCount: _arrayOfData.length,
itemBuilder: (context, i) {
return ListTile(
title: Text(_arrayOfData[i]["name"]),
subtitle: new Text(_arrayOfData[i]["address"]),
leading: new CircleAvatar(
backgroundColor: Colors.blue,
child: new Text(_arrayOfData[i]["name"][0])),
trailing: new Icon(
(_arraySelectedRowStatus[i] == 0
? Icons.favorite_border
: Icons.favorite),
color: (_arraySelectedRowStatus[i] == 0 ? null : Colors.red),
),
onTap: (){
setState(() {
_arraySelectedRowStatus[i] = _arraySelectedRowStatus[i] == 0 ? 1 : 0;
});
Scaffold.of(context).showSnackBar(new SnackBar(
content:
new Text("You are selecting at ${_arraySelectedRowStatus[i]}")));
},
);
},
);
}
}
Couple of problems in your code.
variable _arraySelectedRowStatus is wrongly defined as final
state variable should be declared in the state class rather than in method.
setState((){}) is used like this.
Related
I need to detect TabBar when I swipe then print somethings on console, how I can do that? This is my code.
bottomNavigationBar: new Material(
color: Colors.blueAccent,
child: new TabBar(
onTap: (int index){ setState(() {
_onTap(index);
});},
indicatorColor: Colors.white,
controller: controller,
tabs: <Widget>[
new Tab(icon: new Icon(Icons.shopping_basket)),
new Tab(icon: new Icon(Icons.store)),
new Tab(icon: new Icon(Icons.local_offer)),
new Tab(icon: new Icon(Icons.assignment)),
new Tab(icon: new Icon(Icons.settings)),
],
)
),
You need to add a listener to your tab controller - maybe in initState.
controller.addListener((){
print('my index is'+ controller.index.toString());
});
Swipe functionality is not provided by onTap() function, for that TabController is used
class TabBarDemo extends StatefulWidget {
#override
_TabBarDemoState createState() => _TabBarDemoState();
}
class _TabBarDemoState extends State<TabBarDemo>
with SingleTickerProviderStateMixin {
TabController _controller;
int _selectedIndex = 0;
List<Widget> list = [
Tab(icon: Icon(Icons.card_travel)),
Tab(icon: Icon(Icons.add_shopping_cart)),
];
#override
void initState() {
// TODO: implement initState
super.initState();
// Create TabController for getting the index of current tab
_controller = TabController(length: list.length, vsync: this);
_controller.addListener(() {
setState(() {
_selectedIndex = _controller.index;
});
print("Selected Index: " + _controller.index.toString());
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
bottom: TabBar(
onTap: (index) {
// Should not used it as it only called when tab options are clicked,
// not when user swapped
},
controller: _controller,
tabs: list,
),
title: Text('Tabs Demo'),
),
body: TabBarView(
controller: _controller,
children: [
Center(
child: Text(
_selectedIndex.toString(),
style: TextStyle(fontSize: 40),
)),
Center(
child: Text(
_selectedIndex.toString(),
style: TextStyle(fontSize: 40),
)),
],
),
),
);
}
}
Github Repo:
https://github.com/jitsm555/Flutter-Problems/tree/master/tab_bar_tricks
Output:
Here is a full example. Use a TabController and add a callback using addListener.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
home: MyTabbedPage(),
);
}
}
class MyTabbedPage extends StatefulWidget {
const MyTabbedPage({Key key}) : super(key: key);
#override
_MyTabbedPageState createState() => _MyTabbedPageState();
}
class _MyTabbedPageState extends State<MyTabbedPage> with SingleTickerProviderStateMixin {
var _context;
final List<Tab> myTabs = <Tab>[
Tab(text: 'LEFT'),
Tab(text: 'RIGHT'),
];
TabController _tabController;
#override
void initState() {
super.initState();
_tabController = TabController(vsync: this, length: myTabs.length);
_tabController.addListener(_handleTabSelection);
}
void _handleTabSelection() {
if (_tabController.indexIsChanging) {
switch (_tabController.index) {
case 0:
Scaffold.of(_context).showSnackBar(SnackBar(
content: Text('Page 1 tapped.'),
duration: Duration(milliseconds: 500),
));
break;
case 1:
Scaffold.of(_context).showSnackBar(SnackBar(
content: Text('Page 2 tapped.'),
duration: Duration(milliseconds: 500),
));
break;
}
}
}
#override
void dispose() {
_tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: _tabController,
tabs: myTabs,
),
),
body: Builder(
builder: (context) {
_context = context;
return TabBarView(
controller: _tabController,
children: myTabs.map((Tab tab) {
final String label = tab.text.toLowerCase();
return Center(
child: Text(
'This is the $label tab',
style: const TextStyle(fontSize: 36),
),
);
}).toList(),
);
},
),
);
}
}
You can create wrapper widget
class _DefaultTabControllerListener extends StatefulWidget {
const _DefaultTabControllerListener(
{Key? key, this.onTabSelected, required this.child})
: super(key: key);
final void Function(int index)? onTabSelected;
final Widget child;
#override
_DefaultTabControllerListenerState createState() =>
_DefaultTabControllerListenerState();
}
class _DefaultTabControllerListenerState
extends State<_DefaultTabControllerListener> {
late final void Function()? _listener;
TabController? _tabController;
#override
void initState() {
super.initState();
WidgetsBinding.instance?.addPostFrameCallback((_) {
final tabController = DefaultTabController.of(context)!;
_listener = () {
final onTabSelected = widget.onTabSelected;
if (onTabSelected != null) {
onTabSelected(tabController.index);
}
};
tabController.addListener(_listener!);
});
}
#override
void didChangeDependencies() {
_tabController = DefaultTabController.of(context);
super.didChangeDependencies();
}
#override
void dispose() {
if (_listener != null && _tabController != null) {
_tabController!.removeListener(_listener!);
}
super.dispose();
}
#override
Widget build(BuildContext context) {
return widget.child;
}
}
And wrap TabBar with this widget
DefaultTabController(
child: _DefaultTabControllerListener(
onTabSelected: (index) {
// Handler
},
child: TabBar(.....
We can listen to tab scroll notification by using NotificationListener Widget
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: NotificationListener(
onNotification: (scrollNotification) {
if (scrollNotification is ScrollUpdateNotification) {
_onStartScroll(scrollNotification.metrics);
}
},
child: _buildTabBarAndTabBarViews(),
);
}
_onStartScroll(ScrollMetrics metrics) {
print('hello world');
}
}
I ran into a similar issue and following is what I did to accomplish the same: -
import 'package:flutter/material.dart';
import '../widgets/basic_dialog.dart';
import 'sign_in_form.dart';
import 'sign_up_form.dart';
class LoginDialog extends StatefulWidget {
#override
_LoginDialogState createState() => _LoginDialogState();
}
class _LoginDialogState extends State<LoginDialog>
with SingleTickerProviderStateMixin {
int activeTab = 0;
TabController controller;
#override
void initState() {
super.initState();
controller = TabController(vsync: this, length: 2);
}
#override
Widget build(BuildContext context) {
return NotificationListener(
onNotification: (ScrollNotification notification) {
setState(() {
if (notification.metrics.pixels <= 100) {
controller.index = 0;
} else {
controller.index = 1;
}
});
return true;
},
child: BasicDialog(
child: Container(
height: controller.index == 0
? MediaQuery.of(context).size.height / 2.7
: MediaQuery.of(context).size.height / 1.8,
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TabBar(
controller: controller,
tabs: <Widget>[
Padding(
padding: const EdgeInsets.all(5.0),
child: Text('Sign In'),
),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text('Sign up'),
),
],
),
Expanded(
child: TabBarView(
controller: controller,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: SingleChildScrollView(
child: SignInForm(),
),
),
// If a container is not displayed during the tab switch to tab0, renderflex error is thrown because of the height change.
controller.index == 0
? Container()
: Padding(
padding: const EdgeInsets.all(8.0),
child: SingleChildScrollView(
child: SignUpForm(),
),
),
],
),
)
],
),
),
),
);
}
}
If you are using DefaultTabController and want to listen to updates in TabBar, you can expose the controller using the DefaultTabController.of method and then add a listener to it:
DefaultTabController(
length: 3,
child: Builder(
builder: (BuildContext context) {
final TabController controller = DefaultTabController.of(context)!;
controller.addListener(() {
if (!controller.indexIsChanging) {
print(controller.index);
// add code to be executed on TabBar change
}
});
return Scaffold(...
Here you have a full example:
class TabControllerDemo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: DefaultTabController(
length: 3,
child: Builder(builder: (BuildContext context) {
final TabController controller = DefaultTabController.of(context)!;
controller.addListener(() {
if (!controller.indexIsChanging) {
print(controller.index);
// add code to be executed on TabBar change
}
});
return Scaffold(
appBar: AppBar(
bottom: const TabBar(
tabs: [
Tab(text: "Tab 0"),
Tab(text: "Tab 1"),
Tab(text: "Tab 2"),
],
),
title: const Text('Tabs Demo'),
),
body: const TabBarView(
children: [
Center(child: Text('View 0')),
Center(child: Text('View 1')),
Center(child: Text('View 2')),
],
),
);
})),
);
}
}
You can also check this DartPad LiveDemo.
Warp your tab in BottomNavigationBar . it will give you option onTap() where you can check which tab will clicked.
using this code you will also redirect to particular page when you tap on Tab
import 'package:flutter/material.dart';
class BottomBarList extends StatefulWidget {
#override
_BottomBarListState createState() => _BottomBarListState();
}
class _BottomBarListState extends State<BottomBarList> {
int bottomSelectedIndex = 0;
int _selectedIndex = 0;
List<Widget> _widgetOptions = <Widget>[
AllMovieList(),
MovieDescription(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(),
bottomNavigationBar: bottomBar(),
body:_widgetOptions.elementAt(_selectedIndex),
);
}
bottomBar() {
return BottomNavigationBar(
type: BottomNavigationBarType.shifting,
unselectedItemColor: Colors.grey,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.tv),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(Icons.star),
title: Text('Business'),
),
],
currentIndex: _selectedIndex,
selectedItemColor: Colors.black,
backgroundColor: Colors.orange,
onTap: _onItemTapped,
);
}
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
}
You can use the only scrollNotification (ScrollEndNotification) of the NotificationListener. It covers the tap and swipe actions.
class HandlingTabChanges extends State<JsonTestDetailFrame> with SingleTickerProviderStateMixin {
late final TabController _tabController;
final int _tabLength = 2;
#override
void initState() {
super.initState();
_tabController = TabController(length: _tabLength, vsync: this);
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: _tabLength,
child: Scaffold(
appBar: AppBar(
title: Text("Some title"),
bottom: TabBar(
controller: _tabController,
tabs: [
Icon(Icons.settings),
Icon(Icons.list_alt),
],
),
),
body: NotificationListener(
onNotification: (scrollNotification) {
if (scrollNotification is ScrollEndNotification) _onTabChanged();
return false;
},
child: TabBarView(
controller: _tabController,
children: [
SomeWidget1(),
SomeWidget2(),
],
),
),
),
);
}
void _onTabChanged() {
switch (_tabController.index) {
case 0:
// handle 0 position
break;
case 1:
// handle 1 position
break;
}
}
}
You can disable swiping effect on TabBarView by adding:
physics: NeverScrollableScrollPhysics(),
and declaring one TabController and assigning that to your TabBar and TabBarView:
TabController _tabController;
Want to clone playstore front page appbar scrolling functionality in flutter. Playstore Appbar I'm trying to make a screen which contains static Tabs under SliverAppBar bottom property. I need to create dynamic Tabs and TabBarViews whenever i clicked onto any parent static tab. I did it successfully but i'm facing some problems.
When i click onto any parent tab, I would try to initialize the tabController but the currentIndex remains the same as it was into the previous parent tab.First Tab Second Tab
Each tab body must save its scrolling position.
This my screen code.
class DynamicTabContent {
IconData icon;
String tooTip;
DynamicTabContent.name(this.icon, this.tooTip);
}
int currentTabBlue = 0;
class TestAppHomePage extends StatefulWidget {
#override
TestAppHomePageState createState() => new TestAppHomePageState();
}
class TestAppHomePageState extends State<TestAppHomePage>
with TickerProviderStateMixin {
List<DynamicTabContent> myList = new List();
ScrollController _scrollController = new ScrollController();
TabController _tabControllerBlue;
TabController _tabController;
handleTabChange() {
currentTabBlue = _tabControllerBlue.index;
print("CurrentTab = " + currentTabBlue.toString());
if (_tabControllerBlue.index == 0) {
setState(() {
myList.clear();
myList.add(new DynamicTabContent.name(Icons.favorite, "Favorited"));
myList
.add(new DynamicTabContent.name(Icons.local_pizza, "local pizza"));
this._tabController = new TabController(initialIndex: 0, length: 15, vsync: this);
});
} else if (_tabControllerBlue.index == 1) {
setState(() {
myList.clear();
myList.add(new DynamicTabContent.name(Icons.favorite, "Favorited"));
myList
.add(new DynamicTabContent.name(Icons.local_pizza, "local pizza"));
myList
.add(new DynamicTabContent.name(Icons.local_pizza, "local pizza"));
this._tabController = new TabController(initialIndex: 0, length: 15, vsync: this);
});
} else if (_tabControllerBlue.index == 2) {
setState(() {
myList.clear();
myList.add(new DynamicTabContent.name(Icons.favorite, "Favorited"));
myList.add(new DynamicTabContent.name(Icons.favorite, "Favorited"));
myList
.add(new DynamicTabContent.name(Icons.local_pizza, "local pizza"));
myList
.add(new DynamicTabContent.name(Icons.local_pizza, "local pizza"));
myList
.add(new DynamicTabContent.name(Icons.local_pizza, "local pizza"));
this._tabController = new TabController(initialIndex: 0, length: 15, vsync: this);
});
}
}
#override
void initState() {
print("initState = TestAppHomePage");
myList.add(new DynamicTabContent.name(Icons.favorite, "Favorited"));
myList.add(new DynamicTabContent.name(Icons.local_pizza, "local pizza"));
_tabControllerBlue =
new TabController(initialIndex: 0, length: 3, vsync: this);
_tabControllerBlue.addListener(handleTabChange);
_tabController =
new TabController(initialIndex: 0, length: myList.length, vsync: this);
}
#override
void dispose() {
print("dispose");
// _tabController.removeListener(handleTabChange);
// _tabController.dispose();
super.dispose();
}
Future<void> executeAfterBuild() async {
print("Build: Called Back");
}
#override
Widget build(BuildContext context) {
executeAfterBuild();
return new Scaffold(
body: new NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
return <Widget>[
SliverAppBar(
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: Colors.white,
),
onPressed: null,
),
title: Text('Kitchen'),
floating: true,
pinned: true,
bottom: TabBar(
controller: _tabControllerBlue,
tabs: [
Tab(icon: Icon(Icons.lightbulb_outline), text: "Tab 1"),
Tab(icon: Icon(Icons.lightbulb_outline), text: "Tab 2"),
Tab(icon: Icon(Icons.lightbulb_outline), text: "Tab 3"),
],
),
),
new SliverPersistentHeader(
pinned: true,
delegate: TestTabBarDelegate(controller: _tabController),
),
];
},
body: new TestHomePageBody(
tabController: _tabController,
scrollController: _scrollController,
myList: myList,
),
),
);
}
}
class TestHomePageBody extends StatefulWidget {
TestHomePageBody({this.tabController, this.scrollController, this.myList});
final TabController tabController;
final ScrollController scrollController;
final List<DynamicTabContent> myList;
State<StatefulWidget> createState() {
return TestHomePageBodyState();
}
}
class TestHomePageBodyState extends State<TestHomePageBody> {
Key _key = new PageStorageKey({});
bool _innerListIsScrolled = false;
void _updateScrollPosition() {
if (!_innerListIsScrolled &&
widget.scrollController.position.extentAfter == 0.0) {
setState(() {
_innerListIsScrolled = true;
print("_innerListIsScrolled = true");
});
} else if (_innerListIsScrolled &&
widget.scrollController.position.extentAfter > 0.0) {
setState(() {
_innerListIsScrolled = false;
print("_innerListIsScrolled = false");
// Reset scroll positions of the TabBarView pages
_key = new PageStorageKey({});
});
}
}
#override
void initState() {
widget.scrollController.addListener(_updateScrollPosition);
print("initState = TestHomePageBodyState");
super.initState();
}
#override
void dispose() {
widget.scrollController.removeListener(_updateScrollPosition);
super.dispose();
}
#override
Widget build(BuildContext context) {
return new TabBarView(
controller: widget.tabController,
key: _key,
children: widget.myList.isEmpty
? <Widget>[]
: widget.myList.map(
(dynamicContent) {
return new Card(
child: new Column(
children: <Widget>[
new Container(
height: 450.0,
width: 300.0,
child: new IconButton(
icon: new Icon(dynamicContent.icon, size: 100.0),
tooltip: dynamicContent.tooTip,
onPressed: null,
),
),
Text(dynamicContent.tooTip),
],
),
);
},
).toList(),
);
}
}
class TestTabBarDelegate extends SliverPersistentHeaderDelegate {
TestTabBarDelegate({this.controller});
final TabController controller;
#override
double get minExtent => kToolbarHeight;
#override
double get maxExtent => kToolbarHeight;
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return new Container(
color: Theme.of(context).cardColor,
height: kToolbarHeight,
child: new TabBar(
controller: controller,
isScrollable: true,
labelColor: Theme.of(context).accentColor,
indicatorSize: TabBarIndicatorSize.label,
key: new PageStorageKey<Type>(TabBar),
indicatorColor: Theme.of(context).accentColor,
tabs: List<Widget>.generate(controller.length, (int index) {
print(controller.length);
return new Tab(text: "Excluded Discounted Deals");
}),
),
);
}
#override
bool shouldRebuild(covariant TestTabBarDelegate oldDelegate) {
return oldDelegate.controller != controller;
}
}
One way to solve it is using PageStorage. eg.
In the onTap method you will need to write the state, and when building the widget you will need to read the state, something as follow:
// Reading state:
var tabInternalIndex = PageStorage.of(context).readState(context, identifier: ValueKey('tab3'));
_currentTab = tabInternalIndex == null ? _currentTab : tabInternalIndex;
// Writing State
onTap: (int index) {
setState(() {
_currentTab = index;
PageStorage.of(context).writeState(context, index,
identifier: ValueKey('tab3'));
});
}
Please notice that identifier for every main tab has to be different.
Complete example:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
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> {
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
initialIndex: 1,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
tabs: [
Tab(icon: Icon(Icons.mail)),
Tab(icon: Icon(Icons.contacts)),
Tab(icon: Icon(Icons.info)),
],
),
title: Text('Sample tabs'),
),
body: TabBarView(children: <Widget>[
SubTabs1(),
SubTabs2(),
SubTabs3()
]),
),
);
}
}
class SubTabs1 extends StatefulWidget {
#override
_SubTabs1State createState() => _SubTabs1State();
}
class _SubTabs1State extends State<SubTabs1> {
int _currentTab = 0;
#override
Widget build(BuildContext context) {
var tabInternalIndex = PageStorage.of(context)
.readState(context, identifier: ValueKey('tab1'));
_currentTab = tabInternalIndex == null ? _currentTab : tabInternalIndex;
return DefaultTabController(
initialIndex: _currentTab,
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
onTap: (int index) {
setState(() {
_currentTab = index;
PageStorage.of(context).writeState(context, index,
identifier: ValueKey('tab1'));
});
},
tabs: [
Tab(icon: Icon(Icons.delete)),
Tab(icon: Icon(Icons.delete_forever)),
Tab(icon: Icon(Icons.delete_outline)),
],
),
),
body: TabBarView(
children: <Widget>[
Container(
color: Colors.green[100],
child: Text('Child 5'),
),
Container(
color: Colors.green[300],
child: Text('Child 6'),
),
Container(
color: Colors.green[600],
child: Text('Child 7'),
)
],
)));
}
}
class SubTabs2 extends StatefulWidget {
#override
_SubTabs2State createState() => _SubTabs2State();
}
class _SubTabs2State extends State<SubTabs2> {
int _currentTab = 0;
#override
Widget build(BuildContext context) {
var tabInternalIndex = PageStorage.of(context)
.readState(context, identifier: ValueKey('tab2'));
_currentTab = tabInternalIndex == null ? _currentTab : tabInternalIndex;
return DefaultTabController(
initialIndex: _currentTab,
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
onTap: (int index) {
setState(() {
_currentTab = index;
PageStorage.of(context).writeState(context, index,
identifier: ValueKey('tab2'));
});
},
tabs: [
Tab(icon: Icon(Icons.alarm_add)),
Tab(icon: Icon(Icons.alarm_off)),
Tab(icon: Icon(Icons.alarm_on)),
],
),
),
body: TabBarView(
children: <Widget>[
Container(
color: Colors.yellow[100],
child: Text('Child 5'),
),
Container(
color: Colors.yellow[300],
child: Text('Child 6'),
),
Container(
color: Colors.yellow[600],
child: Text('Child 7'),
)
],
)));
}
}
class SubTabs3 extends StatefulWidget {
#override
_SubTabs3State createState() => _SubTabs3State();
}
class _SubTabs3State extends State<SubTabs3> {
int _currentTab = 0;
#override
Widget build(BuildContext context) {
// Reading state:
var tabInternalIndex = PageStorage.of(context).readState(context, identifier: ValueKey('tab3'));
_currentTab = tabInternalIndex == null ? _currentTab : tabInternalIndex;
return DefaultTabController(
initialIndex: _currentTab,
length: 3,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
onTap: (int index) {
setState(() {
_currentTab = index;
PageStorage.of(context).writeState(context, index,
identifier: ValueKey('tab3'));
});
},
tabs: [
Tab(icon: Icon(Icons.ac_unit)),
Tab(icon: Icon(Icons.accessible)),
Tab(icon: Icon(Icons.airport_shuttle)),
],
),
),
body: TabBarView(
children: <Widget>[
Container(
color: Colors.pink[100],
child: Text('Child 1'),
),
Container(
color: Colors.pink[300],
child: Text('Child 2'),
),
Container(
color: Colors.pink[600],
child: Text('Child 3'),
)
],
)));
}
}
Hope this help.
Alright, i have a big issue with this.
Most of the Questions with similar issues, didn't quite help me cause they had a very different approach.
I'm trying to edit the data in a Profile. Im using a Map to access the Data.
Now when i try to set the new Map an Error appears: "Only static members can be accessed in initializers."
i show you all the code. its not everything and i changed some parts to see better what i mean. i hope you guys understand it.
class Tabs extends StatefulWidget {
Tabs({this.infos});
final Map<String, dynamic> info;
#override
State<StatefulWidget> createState() {
return _Tabs();
}
}
class _Tabs extends State<Tabs> {
int _currentIndex = 0; //Bottom Navigation
final List<Widget> _children = [
MaterialApp(
theme: ThemeData(
primaryColor: Colors.white,
primaryColorDark: Colors.cyan[700],
primaryColorLight: Colors.cyan[200],
accentColor: Colors.deepOrange[200],
),
home: Suche(), //not important..
),
Center(child: Text("2")), //some dummy page
Chats(),// some other dummy page
Profile(userInformationen: _infosTabs, updateProfile: _updateProfile), //the Profile Page plus the Error appears "Only static members can be accessed in initializers."
];
void _updateProfil(Map<String, dynamic> newMap) {
setState(() {
_infos = newMap;
});
} //here i want to update the info i receive
final Map<String, dynamic> _infos = ({
'title': "Title",
'name': "name",
'points': 73,
'about me': "texttexttexttexttext"
});
class Profile extends StatelessWidget {
Profile({this.userInformationen, this.updateProfile});
final Map<String, dynamic> userInformationen;
final Function updateProfile;
#override
Widget build(BuildContext context) {
double containerHight = 8.0;
return Scaffold(
drawer: Drawer(
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Text("Info"),
),
body: ListView(
children: <Widget>[
ListTile(
leading: Icon(Icons.settings),
title: Text("Profil bearbeiten"),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => EditProfile(
updateProfile: updateProfile),
),
],
),
],
),
),
);
}
}
class EditProfile extends StatefulWidget {
final Function updateProfile;
EditProfile({this.updateProfile});
#override
State<StatefulWidget> createState() {
return _EditProfileStae();
}
}
class _EditProfileState extends State<EditProfile> {
_EditProfileState();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Edit Profile')),
body: Container(
margin: EdgeInsets.all(16.0),
child: Form(
child: ListView(
children: <Widget>[
TextField(
),
RaisedButton(
child: Text("Save"),
onPressed: () {
widget.updateProfile({
'title': 'Title',
'name': 'some name',
'points': 69,
'about': "texttexttext"
});
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (BuildContext context) => Tabs(),
),
);
},
),
],
),
),
),
);
}
}
i dont know may be i have a complet wrong approach or its just a little mistake.
You need to do something like the following. I replaced some of the widgets you didn't define with just Text.
Note that children is no longer final, you need to set it in initState. Also note that _infos cannot be final if you want to use the _updateProfile in the way you have it currently defined.
class _Tabs extends State<Tabs> {
List<Widget> _children;
void _updateProfile(Map<String, dynamic> newMap) {
setState(() {
_infos = newMap;
});
} //here i want to update the info i receive
Map<String, dynamic> _infos = ({
'title': "Title",
'name': "name",
'points': 73,
'about me': "texttexttexttexttext"
});
#override
void initState() {
_children = [
MaterialApp(
theme: ThemeData(
primaryColor: Colors.white,
primaryColorDark: Colors.cyan[700],
primaryColorLight: Colors.cyan[200],
accentColor: Colors.deepOrange[200],
),
home: Text(''), //not important..
),
Center(child: Text("2")), //some dummy page
Text(''), // some other dummy page
Profile(
userInformationen: _infosTabs,
updateProfile:
_updateProfile), //the Profile Page plus the Error appears "Only static members can be accessed in initializers."
];
super.initState();
}
}
I am trying to build a dynamic TextField list in flutter but did not understand at how I can handle it.
My list looks like this:
My code structure is:
ListView.builder(
shrinkWrap: true,
itemCount: itemList.length,
itemBuilder: (context, index) {
if(itemList.length == null) {
return _buildProgressIndicator();
} else {
return singleItemList(index);
}
})
Widget singleItemList(int index) {
if((itemList.length -1) < index)
{
return Container(child: null);
} else {
dynamic singleItem = itemList[index];
String counter = (index+1).toString();
return
Container(
decoration: BoxDecoration(
color: Colors.white,
),
child: Row(
children:[
Expanded(
flex: 1,
child: Text(counter,style: orderBookItemListCounter(),)
),
Expanded(
flex: 3,
child: TextField(
controller: _addProductToCart(counter),
decoration: InputDecoration(
labelText: "Qty",
),
)
),
])
);
}
}
My product list is not fixed may change the number of products,
I would like to capture Qty as per product ID.
Thanks
:)
You have several options depending how you architecture your application or where you have your central state.
I propose you here a solution that updates a local map variable. Alternatively, you could send an event/stream to the place your store is located.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Item> itemList = [
Item("ID1", "First product"),
Item("ID2", "Second product"),
];
Map<String, int> quantities = {};
void takeNumber(String text, String itemId) {
try {
int number = int.parse(text);
quantities[itemId] = number;
print(quantities);
} on FormatException {}
}
Widget singleItemList(int index) {
Item item = itemList[index];
return Container(
decoration: BoxDecoration(
color: Colors.white,
),
child: Row(
children: [
Expanded(flex: 1, child: Text("${index + 1}")),
Expanded(
flex: 3,
child: TextField(
keyboardType: TextInputType.number,
onChanged: (text) {
takeNumber(text, item.id);
},
decoration: InputDecoration(
labelText: "Qty",
),
),
),
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Demo")),
body: Center(
child: ListView.builder(
shrinkWrap: true,
itemCount: itemList.length,
itemBuilder: (context, index) {
if (itemList.isEmpty) {
return CircularProgressIndicator();
} else {
return singleItemList(index);
}
}),
),
);
}
}
class Item {
final String id;
final String name;
Item(this.id, this.name);
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<TextEditingController> _controllers = new List();
List<Item> itemList = [
Item("ID1", "product 1"),
Item("ID2", "product 2"),
Item("ID3", "product 3"),
Item("ID4", "product 4"),
Item("ID5", "product 5"),
Item("ID6", "product 6"),
Item("ID7", "product 7"),
Item("ID8", "product 8"),
Item("ID9", "product 9"),
Item("ID10", "product 10"),
Item("ID11", "product 11"),
Item("ID12", "product 12"),
Item("ID13", "product 13"),
Item("ID14", "product 14"),
Item("ID15", "product 15"),
Item("ID16", "product 16"),
Item("ID17", "product 17"),
Item("ID18", "product 18"),
];
Map<String, int> quantities = {};
void takeNumber(String text, String itemId) {
try {
int number = int.parse(text);
quantities[itemId] = number;
print(quantities);
} on FormatException {}
}
Widget singleItemList(int index, TextEditingController controllertxt) {
Item item = itemList[index];
return Container(
decoration: BoxDecoration(
color: Colors.white,
),
child: Row(
children: [
Expanded(flex: 1, child: Text("${index + 1}")),
Expanded(
flex: 3,
child: new TextField(
controller: controllertxt,
keyboardType: TextInputType.number,
onChanged: (text) {
takeNumber(text, item.id);
},
decoration: InputDecoration(
labelText: "Qty",
),
),
),
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Demo")),
body: Center(
child: ListView.builder(
shrinkWrap: true,
itemCount: itemList.length,
itemBuilder: (context, index) {
_controllers.add(new TextEditingController());
if (itemList.isEmpty) {
return CircularProgressIndicator();
} else {
return singleItemList(index,_controllers[index]);
}
}),
),
);
}
}
class Item {
final String id;
final String name;
Item(this.id, this.name);
}
class _SearchState extends State<Search> {
#override
Widget build(BuildContext context) {
widget.id;
return new Scaffold(
appBar: new AppBar(
actions: <Widget>[
new IconButton(
icon: new Icon(Icons.exit_to_app),
onPressed: _getTicketDetails
)
],
centerTitle: true,
title: new Text
("TicketsDetails", style: const TextStyle(
fontFamily: 'Poppins'
,),),),
);
}
_getTicketDetails() async {
print(widget.id);
var userDetails = {};
final response = await http.get(
"https:...", headers: {
HttpHeaders.AUTHORIZATION:
"...
});
List returnTicketDetails = json.decode(response.body);
print(returnTicketDetails);
for (var i = 0; i < (returnTicketDetails?.length ?? 0); i++) {
final ticketresponse = await http.get(
"..${returnTicketDetails[i]["user_id"]
.toString()}", headers: {
HttpHeaders.AUTHORIZATION:
"...
});
userDetails[returnTicketDetails[i]["user_id"]] =
json.decode(ticketresponse.body);
}
print(userDetails);
}
Hi there! In the console the output I get by printing (userDetails) is: {513549601: {first_name: Test, last_name: Account, profile_image: tempURL. However, I would like to create a Listview dynamically with: userDetails[user_id][first_name] userDetails[user_id][last_name] and so on... But my concern is, where am I suppose to implement the Listview? As I already have a Widget build used at the very top.
Try this!
Let's assume you got the data from API say userdetails list. Then you just need an ListView.builder() and Custom UserWidget/ListTile .
The Result :
The Code :
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(home: new Home()));
}
class Home extends StatelessWidget {
List userdetails = [
{
"first_name": "FLUTTER",
"last_name": "AWESOME",
"image_url": "https://pbs.twimg.com/profile_images/760249570085314560/yCrkrbl3_400x400.jpg"
},
{
"first_name": "ABC",
"last_name": "XYZ",
"image_url": "https://www.rd.com/wp-content/uploads/2017/09/01-shutterstock_476340928-Irina-Bg-1024x683.jpg"
},
{
"first_name": "FLUTTER",
"last_name": "AWESOME",
"image_url": "https://pbs.twimg.com/profile_images/760249570085314560/yCrkrbl3_400x400.jpg"
},
{
"first_name": "ABC",
"last_name": "XYZ",
"image_url": "https://www.rd.com/wp-content/uploads/2017/09/01-shutterstock_476340928-Irina-Bg-1024x683.jpg"
},
{
"first_name": "FLUTTER",
"last_name": "AWESOME",
"image_url": "https://pbs.twimg.com/profile_images/760249570085314560/yCrkrbl3_400x400.jpg"
},
{
"first_name": "ABC",
"last_name": "XYZ",
"image_url": "https://www.rd.com/wp-content/uploads/2017/09/01-shutterstock_476340928-Irina-Bg-1024x683.jpg"
},
{
"first_name": "FLUTTER",
"last_name": "AWESOME",
"image_url": "https://pbs.twimg.com/profile_images/760249570085314560/yCrkrbl3_400x400.jpg"
},
{
"first_name": "ABC",
"last_name": "XYZ",
"image_url": "https://www.rd.com/wp-content/uploads/2017/09/01-shutterstock_476340928-Irina-Bg-1024x683.jpg"
},
];
#override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Colors.grey,
appBar: new AppBar(
title: new Text("Dynamic List"),
backgroundColor: Colors.redAccent,
),
body: new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new UserWidget(
firstName: userdetails[index]['first_name'],
lastName: userdetails[index]['last_name'],
imageURL: userdetails[index]['image_url'],
);
},
itemCount: userdetails.length,
),
);
}
}
class UserWidget extends StatelessWidget {
final String firstName;
final String lastName;
final String imageURL;
const UserWidget({Key key, this.firstName, this.lastName, this.imageURL}) : super(key: key);
#override
Widget build(BuildContext context) {
return new Container(
decoration: new BoxDecoration(border: new Border.all(width: 1.0, color: Colors.grey), color: Colors.white70),
margin: new EdgeInsets.symmetric(vertical: 1.0),
child: new ListTile(
leading: new FadeInImage(
placeholder: new AssetImage('assets/me.jpg'),
image: new NetworkImage(imageURL),
),
title: new Text("First Name : " + firstName),
subtitle: new Text("Last Name : " + lastName),
),
);
}
}
Also, check out FadeInImage it gives a placeholder[shows local asset till the image load] for network image.