Where content of StatefulWidget should be stored? - flutter

I switch two layout by BottomNavigationBar
Every time switching the bar, it loads initState() and lost all variables in _BodyLayoutState()
So, I wonder,
1.Keeping the contents(List<Article> articles = [];) in State is not good?? I should keep contents in upper class like _MyHomePageState?
2.Is there any way to keep contents in State and not dispose when switching ???
These are source code.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
"/": (_) => new MyHomePage(),
"/browser": (_) => new Text("not use"),
}
);
}
}
class Article{
String title;
String url;
Article();
}
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<Widget> _myLayouts = [];
int _currentIndex = 0;
#override
void initState() {
super.initState();
_myLayouts = [
new BodyLayout("latest", key: Key('1')),
new BodyLayout("pop",key: Key('2')),
];
}
void _onItemTapped(int index) {
print("itemTapped :" +index.toString());
setState(() {
_currentIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// title: Text(widget.title),
),
body: _myLayouts[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('latest'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
title: Text('pop'),
),
],
currentIndex: _currentIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
class BodyLayout extends StatefulWidget {
final String mode;
BodyLayout(this.mode, {Key key}) : super(key: key);
#override
_BodyLayoutState createState() => _BodyLayoutState();
}
class _BodyLayoutState extends State<BodyLayout>{
List<Article> articles = [];
bool loading = true;
bool firstLoaded = false;
int page = 1;
#override
void initState(){
super.initState();
print ("init:" + widget.mode);
if (firstLoaded == false){
print("I don't want to load twice");
_callApi();
}
}
void _callApi() {
var a = Article();
a.title = widget.mode;
a.url = widget.mode;
articles.add(a);
firstLoaded == true;
setState((){
loading = false;
});
}
#override
Widget build(BuildContext context) {
if(loading) {
return CircularProgressIndicator();
}
return ListView.builder(
itemCount: articles.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(articles[index].title),
);
},
);
}
}

You can try IndexedStack to display your BodyLayout's.
Just change
body: _myLayouts[_currentIndex],
to
body: IndexedStack(children: _myLayouts, index: _currentIndex,),

You can use the Provider package to store that information https://pub.dev/packages/provider

Related

How to Make flutter webview load only once?

when I switch from one page to another the state of my WebView isn't remembered and it reloads every time. how can prevent that?
I was expecting the web view to load only for the first time and remember it's state but it gets reloaded every time i navigate to that screen. how to make it remember the state?
You can achieve this with PageView with AutomaticKeepAliveClientMixin.
See this example app:
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final bucket = PageStorageBucket();
MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
final PageController _controller = PageController();
int _selectedIndex = 0;
void _onItemTapped(int index) {
setState(() {
_controller.jumpToPage(index);
_selectedIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
// selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'home'),
BottomNavigationBarItem(icon: Icon(Icons.web), label: 'web'),
]),
body: PageView(
controller: _controller,
children: const [MyDummyPage(), MyWebView()],
),
);
}
}
class MyDummyPage extends StatefulWidget {
const MyDummyPage({super.key});
#override
State<MyDummyPage> createState() => _MyDummyPageState();
}
class _MyDummyPageState extends State<MyDummyPage>
with AutomaticKeepAliveClientMixin {
late int count;
#override
void initState() {
super.initState();
count = 0;
}
#override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('$count'),
],
),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () => setState(() {
count++;
}),
),
);
}
#override
bool get wantKeepAlive => true;
}
class MyWebView extends StatefulWidget {
const MyWebView({super.key});
#override
State<MyWebView> createState() => _MyWebViewState();
}
class _MyWebViewState extends State<MyWebView>
with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
super.build(context);
return const WebView(initialUrl: 'https://flutter.dev');
}
#override
bool get wantKeepAlive => true;
}

Cupertino Tab Bar

I am trying to implement the Cupertino bottom tab bar. I have 2 issues. When I call the widgetBuilder in the Cupertino tab view I get 'The function can't be unconditionally invoked because it can be 'null'', so add a null check '!' but then it requires another one.
Then, when I try to return the CupertinoHomeScaffold with currentTab, onSelectTab, widgetBuilder as field it says parameter 'key' is required. Not sure what I should do.
Let me know if you see anything! Thanks in advance..!
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
enum TabItem { jobs, entries, account }
class TabItemData {
const TabItemData({required this.title, required this.icon});
final String title;
final IconData icon;
static const Map<TabItem, TabItemData> allTabs = {
TabItem.jobs: TabItemData(title: 'Jobs', icon: Icons.work),
TabItem.entries: TabItemData(title: 'entries', icon: Icons.view_headline),
TabItem.account: TabItemData(title: 'entries', icon: Icons.person),
};
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
TabItem _currentTab = TabItem.jobs;
Map<TabItem, WidgetBuilder> get widgetBuilder {
return {
TabItem.jobs: (_) => Container(),
TabItem.entries: (_) => Container(),
TabItem.account: (_) => Container(),
};
}
void _select(TabItem tabItem) {
setState(() => _currentTab = tabItem);
}
#override
Widget build(BuildContext context) {
return CupertinoHomeScaffold(
currentTab: _currentTab,
onSelectTab: _select,
widgetBuilder: widgetBuilder,
);
}
}
class CupertinoHomeScaffold extends StatelessWidget {
const CupertinoHomeScaffold({
required Key key,
required this.currentTab,
required this.onSelectTab,
required this.widgetBuilder,
}) : super(key: key);
final TabItem currentTab;
final ValueChanged<TabItem> onSelectTab;
final Map<TabItem, WidgetBuilder> widgetBuilder;
#override
Widget build(BuildContext context) {
return CupertinoTabScaffold(
tabBar: CupertinoTabBar(
items: [
_buildItem(TabItem.jobs),
_buildItem(TabItem.entries),
_buildItem(TabItem.account),
],
onTap: (index) => onSelectTab(TabItem.values[index]),
),
tabBuilder: (context, index) {
final item = TabItem.values[index];
return CupertinoTabView(
builder: (context) => widgetBuilder![item](context),
);
},
);
}
BottomNavigationBarItem _buildItem(TabItem tabItem) {
final itemData = TabItemData.allTabs[tabItem];
return BottomNavigationBarItem(
icon: Icon(itemData!.icon),
label: itemData.title,
);
}
}
Make key a nullable variable:
Key? key,
For widgetBuilder, you need to put ! after ]:
widgetBuilder[item]!(context),
Working Example
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() => runApp(
MaterialApp(
home: HomePage(),
),
);
enum TabItem { jobs, entries, account }
class TabItemData {
const TabItemData({required this.title, required this.icon});
final String title;
final IconData icon;
static const Map<TabItem, TabItemData> allTabs = {
TabItem.jobs: TabItemData(title: 'Jobs', icon: Icons.work),
TabItem.entries: TabItemData(title: 'entries', icon: Icons.view_headline),
TabItem.account: TabItemData(title: 'entries', icon: Icons.person),
};
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
TabItem _currentTab = TabItem.jobs;
Map<TabItem, WidgetBuilder> get widgetBuilder {
return {
TabItem.jobs: (_) => Container(),
TabItem.entries: (_) => Container(),
TabItem.account: (_) => Container(),
};
}
void _select(TabItem tabItem) {
setState(() => _currentTab = tabItem);
}
#override
Widget build(BuildContext context) {
return CupertinoHomeScaffold(
currentTab: _currentTab,
onSelectTab: _select,
widgetBuilder: widgetBuilder,
);
}
}
class CupertinoHomeScaffold extends StatelessWidget {
const CupertinoHomeScaffold({
Key? key,
required this.currentTab,
required this.onSelectTab,
required this.widgetBuilder,
}) : super(key: key);
final TabItem currentTab;
final ValueChanged<TabItem> onSelectTab;
final Map<TabItem, WidgetBuilder> widgetBuilder;
#override
Widget build(BuildContext context) {
return CupertinoTabScaffold(
tabBar: CupertinoTabBar(
items: [
_buildItem(TabItem.jobs),
_buildItem(TabItem.entries),
_buildItem(TabItem.account),
],
onTap: (index) => onSelectTab(TabItem.values[index]),
),
tabBuilder: (context, index) {
final item = TabItem.values[index];
return CupertinoTabView(
builder: (context) => widgetBuilder[item]!(context),
);
},
);
}
BottomNavigationBarItem _buildItem(TabItem tabItem) {
final itemData = TabItemData.allTabs[tabItem];
return BottomNavigationBarItem(
icon: Icon(itemData!.icon),
label: itemData.title,
);
}
}

How to redraw StatefulWidget

On the example below, since MyStatefulWidget has a state, it doesn't matter if setState is called on _MyAppState, because it will not be redrawn.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
int value = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('App Example')),
body: Row(children:[
MyStatefulWidget(title: value.toString()),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: (){setState(() { value+=1; });},
child: new Text("Add"),
)
]),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key, this.title}):super(key: key);
final String title;
#override
State<StatefulWidget> createState() {
return _MyStatefulWidgetState();
}
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
String title;
#override
void initState() {
super.initState();
if (widget.title!=null) {
title = widget.title;
} else {
title = "";
}
}
int value = 0;
#override
Widget build(BuildContext context) {
return Text(title);
}
}
If I used a StatelessWidget it'd be redrawn, but this is just an example, there are cases where I need to redraw a StatefulWidget when setState is called.
One option would be to give it a name and build it from the setState, but I need it to be draw in the place where it's draw right now.
Dartpad: https://dartpad.dev/968be8755d5deab1ca5c8c84a993eafc
You could directly use widget.title in the Text widget to update the counter on screen. Please see the code below :
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
void changeVal(int val) {
setState(() {
value = val;
});
}
int value = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('App Example')),
body: Row(children: [
MyStatefulWidget(
title: value.toString(),
groupValue: value % 10,
chnageVal: changeVal),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: () {
setState(() {
value += 1;
});
},
child: const Text("Add"),
)
]),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key key, this.title, this.groupValue, this.chnageVal})
: super(key: key);
final String title;
final int groupValue;
final Function(int) chnageVal;
#override
State<StatefulWidget> createState() {
return _MyStatefulWidgetState();
}
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
//String title;
// #override
// void initState() {
// super.initState();
// if (widget.title!=null) {
// title = widget.title;
// } else {
// title = "";
// }
// }
//int value = 0;
List<int> numbers = List.generate(10, (index) => index);
#override
Widget build(BuildContext context) {
return Container(
width: 120,
child: Column(children: [
Text(widget.title),
...numbers
.map((number) => RadioListTile<int>(
title: Text('$number'),
value: number,
groupValue: widget.groupValue,
onChanged: (val) {
widget.chnageVal(val);
},
))
.toList()
]),
);
}
}
Just provide a unique key while calling MyStatefulWidget like MyStatefulWidget(key: UniqueKey(), title: value.toString()),.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
int value = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('App Example')),
body: Row(children: [
MyStatefulWidget(key: UniqueKey(), title: value.toString()),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: () {
setState(() {
value += 1;
});
},
child: new Text("Add"),
)
]),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key, this.title}) : super(key: key);
final String title;
#override
State<StatefulWidget> createState() {
return _MyStatefulWidgetState();
}
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
String title;
#override
void initState() {
super.initState();
if (widget.title != null) {
title = widget.title;
} else {
title = "";
}
}
int value = 0;
#override
Widget build(BuildContext context) {
return Text(title);
}
}
To know more about key please go through this article.
I will recommend using Stream, better performance and not so hard to use for refresh partial UI.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
StreamController<int> _streamController = StreamController();
int value = 0;
#override
void dispose() {
_streamController.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home:
StreamBuilder<int>(
stream: _streamController.stream,
initialData: value,
builder:
(BuildContext context, AsyncSnapshot<int> snapshot) {
return Scaffold(
appBar: AppBar(title: Text('App Example')),
body: Row(children:[
MyStatefulWidget(title: value.toString()),
RaisedButton(
textColor: Colors.white,
color: Colors.blue,
onPressed: (){_streamController.sink.add(value++);},
child: new Text("Add"),
)
]),
);},),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key, this.title}):super(key: key);
final String title;
#override
State<StatefulWidget> createState() {
return _MyStatefulWidgetState();
}
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
String title;
#override
void initState() {
super.initState();
if (widget.title!=null) {
title = widget.title;
} else {
title = "";
}
}
int value = 0;
#override
Widget build(BuildContext context) {
return Text(title);
}
}

Behaivor of instances new-ed from the same StatefulWidget

I have troubled with the behaivor of instances from the same class.
I made two instances of StatefulWidget(BodyLayout) class.
and switch them by BottomNavigationBar
But only one of initState() of BodyLayout is called.
I am confused by this behavior , State is shared each instances???
I want to each initState() is called separately.
Please help some hint.
These are full source code below.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
routes: {
"/": (_) => new MyHomePage(),
"/browser": (_) => new Text("not use"),
}
);
}
}
class Article{
String title;
String url;
Article();
}
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<Widget> _myLayouts = [];
int _currentIndex = 0;
#override
void initState() {
super.initState();
_myLayouts = [
new BodyLayout("latest"),
new BodyLayout("pop"),
];
}
void _onItemTapped(int index) {
print("itemTapped :" +index.toString());
setState(() {
_currentIndex = index;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// title: Text(widget.title),
),
body: _myLayouts[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text('latest'),
),
BottomNavigationBarItem(
icon: Icon(Icons.business),
title: Text('pop'),
),
],
currentIndex: _currentIndex,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
),
);
}
}
class BodyLayout extends StatefulWidget {
final String mode;
BodyLayout(this.mode);
#override
_BodyLayoutState createState() => _BodyLayoutState();
}
class _BodyLayoutState extends State<BodyLayout>{
List<Article> articles = [];
bool loading = true;
int page = 1;
#override
void initState(){
super.initState();
print ("init:" + widget.mode);// this called only one time.....
_callApi(); // this called only one time.....
}
void _callApi() {
var a = Article();
a.title = widget.mode;
a.url = widget.mode;
articles.add(a);
setState((){
loading = false;
});
}
#override
Widget build(BuildContext context) {
if(loading) {
return CircularProgressIndicator();
}
return ListView.builder(
itemCount: articles.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(articles[index].title),
);
},
);
}
}
I am confused by this behavior , State is shared each instances???
Yes, it is. You need to provide unique keys to your widgets:
class BodyLayout extends StatefulWidget {
BodyLayout(this.mode, {Key key}) : super(key: key);
...
_myLayouts = [
new BodyLayout("latest", key: Key('1')),
new BodyLayout("pop", key: Key('2')),
];
and bam - it works:
I/flutter (12871): init:latest
I/flutter (12871): itemTapped :1
I/flutter (12871): init:pop
From the docs:
A StatefulWidget keeps the same State object when moving from one location in the tree to another if its creator used a GlobalKey for its key.
So basically, if your widgets have the same key (or don't have one), they're interpreted as the same widget.

Flutter Quick Actions change selected Bottom Navigation Bar item

I'm trying to implement home screen quick actions / app shortcuts in my Flutter app. What I'm trying to achieve is when the user launches my app via a quick action, the app changes the selected tab inside the bottom navigation bar. Any help is appreciated.
main.dart:
runApp(
MaterialApp(
theme: Themes.appLightTheme,
darkTheme: Themes.appDarkTheme,
home: QuickActionsController(
child: HomeFrame(currentIndex: 0),
),
My QuickActionsController class:
import 'package:binfinder/screens/HomeFrame.dart';
import 'package:flutter/material.dart';
import 'package:quick_actions/quick_actions.dart';
class QuickActionsController extends StatefulWidget {
final HomeFrame child;
QuickActionsController({Key key, this.child}) : super(key: key);
#override
_QuickActionsControllerState createState() => _QuickActionsControllerState();
}
class _QuickActionsControllerState extends State<QuickActionsController> {
final QuickActions quickActions = QuickActions();
int _currentIndex = 0;
#override
void initState() {
super.initState();
_handleQuickActions();
_setupQuickActions();
}
void _setupQuickActions() {
quickActions.setShortcutItems(<ShortcutItem>[
ShortcutItem(
type: 'action_map',
localizedTitle: 'Map',
),
]);
}
void _handleQuickActions() {
quickActions.initialize((shortcutType) {
if (shortcutType == 'action_map') {
setState(() {
_currentIndex = 1;
});
} else {
setState(() {
_currentIndex = 0;
});
}
});
}
#override
Widget build(BuildContext context) {
widget.child.currentIndex = _currentIndex;
return widget.child;
}
}
In the demo below, direct click app will enter First Page and In Quick Action choose Main view will enter Second Page
_handleQuickActions need to use
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => BottomNavigationBarController(
initialIndex: 1,
)));
and use initial index to control page index
class BottomNavigationBarController extends StatefulWidget {
final int initialIndex;
BottomNavigationBarController({
this.initialIndex,
Key key,
}) : super(key: key);
#override
_BottomNavigationBarControllerState createState() =>
_BottomNavigationBarControllerState();
}
full code
import 'package:flutter/material.dart';
import 'package:quick_actions/quick_actions.dart';
import 'dart:io';
class QuickActionsManager extends StatefulWidget {
final Widget child;
QuickActionsManager({Key key, this.child}) : super(key: key);
_QuickActionsManagerState createState() => _QuickActionsManagerState();
}
class _QuickActionsManagerState extends State<QuickActionsManager> {
final QuickActions quickActions = QuickActions();
#override
void initState() {
super.initState();
_setupQuickActions();
_handleQuickActions();
}
#override
Widget build(BuildContext context) {
return widget.child;
}
void _setupQuickActions() {
quickActions.setShortcutItems(<ShortcutItem>[
ShortcutItem(
type: 'action_main',
localizedTitle: 'Main view',
icon: Platform.isAndroid ? 'quick_box' : 'QuickBox'),
ShortcutItem(
type: 'action_help',
localizedTitle: 'Help',
icon: Platform.isAndroid ? 'quick_heart' : 'QuickHeart')
]);
}
void _handleQuickActions() {
quickActions.initialize((shortcutType) {
if (shortcutType == 'action_main') {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => BottomNavigationBarController(
initialIndex: 1,
)));
} else if (shortcutType == 'action_help') {
print('Show the help dialog!');
}
});
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'QuickActions Demo',
home: QuickActionsManager(child: BottomNavigationBarController(initialIndex: 0,)));
}
}
class Home extends StatelessWidget {
const Home({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(body: Center(child: Text('Home')));
}
}
class Login extends StatelessWidget {
const Login({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(body: Center(child: Text('Login')));
}
}
class BottomNavigationBarController extends StatefulWidget {
final int initialIndex;
BottomNavigationBarController({
this.initialIndex,
Key key,
}) : super(key: key);
#override
_BottomNavigationBarControllerState createState() =>
_BottomNavigationBarControllerState();
}
class _BottomNavigationBarControllerState
extends State<BottomNavigationBarController> {
final List<Widget> pages = [
FirstPage(
key: PageStorageKey('Page1'),
),
SecondPage(
key: PageStorageKey('Page2'),
),
];
final PageStorageBucket bucket = PageStorageBucket();
int _selectedIndex = 0;
Widget _bottomNavigationBar(int selectedIndex) => BottomNavigationBar(
onTap: (int index) => setState(() => _selectedIndex = index),
currentIndex: selectedIndex,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.add), title: Text('First Page')),
BottomNavigationBarItem(
icon: Icon(Icons.list), title: Text('Second Page')),
],
);
#override
void initState() {
_selectedIndex = widget.initialIndex;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: _bottomNavigationBar(_selectedIndex),
body: PageStorage(
child: pages[_selectedIndex],
bucket: bucket,
),
);
}
}
class FirstPage extends StatelessWidget {
const FirstPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("First Screen"),
),
body: ListView.builder(itemBuilder: (context, index) {
return ListTile(
title: Text('Lorem Ipsum'),
subtitle: Text('$index'),
);
}),
);
}
}
class SecondPage extends StatelessWidget {
const SecondPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Screen"),
),
body: ListView.builder(itemBuilder: (context, index) {
return ListTile(
title: Text('Lorem Ipsum'),
subtitle: Text('$index'),
);
}),
);
}
}
demo, emulator is a little slow when enter Second Page