Flutter Quick Actions change selected Bottom Navigation Bar item - flutter

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

Related

How does the "event" parameter contain data? What should I do if I want to create my "onHover"?

I have a simple flutter application. It's ok, but I'm trying to understand how onHover: (event){...} works, why "event" contains data? How can I make my own widget have function parameters like that?
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
double dx = 0, dy = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Title',
home: Scaffold(
body: MouseRegion(
onHover: (event) {
setState(() {
dx = event.localPosition.dx;
dy = event.localPosition.dy;
});
},
child: Center(
child: Text('$dx'),
),
),
),
);
}
}
To create your own onChange, or the like we can use ValueChanged.
For example, taking a look at the code for a TextButton() we see:
const TextButton({
Key? key,
required VoidCallback? onPressed,
VoidCallback? onLongPress,
ValueChanged<bool>? onHover,
the onHover uses a ValueChanged.
You can implement your own valueChanged using this example:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Buttons(
onHover: (value) {
// Do something
print(value);
},
),
),
),
);
}
}
class Buttons extends StatelessWidget {
final ValueChanged<String> onHover;
Buttons({Key? key, required this.onHover}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: [
TextButton(
onPressed: () {
onHover('Pressed');
},
child: Text("Click me")),
Text('hi')
],
);
}
}
So this how we pass the data from the widget which is at the bottom of the widget tree.
It's more related to passing the value from bottom to top using callback functions.
Below is the simple example to demonstrate this data sharing.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
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: const MyStatefulWidget(),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
int _parentData = 0;
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(
"Parent State Value: " + _parentData.toString(),
),
ChildWidgetExample(
callbackFn: (data) {
setState(() {
_parentData = data;
});
},
)
],
);
}
}
class ChildWidgetExample extends StatefulWidget {
final Function(int) callbackFn;
const ChildWidgetExample({
Key? key,
required this.callbackFn,
}) : super(key: key);
#override
State<ChildWidgetExample> createState() => _ChildWidgetExampleState();
}
class _ChildWidgetExampleState extends State<ChildWidgetExample> {
int data = 0;
#override
Widget build(BuildContext context) {
return Column(
children: [
Text(
data.toString(),
),
const SizedBox(
height: 30,
),
ElevatedButton(
onPressed: () {
setState(() {
data++;
});
widget.callbackFn(data);
},
child: const Text("Press"),
)
],
);
}
}
In Flutter you can declare Functions with parameters.
void Function(String foo) myFunction;
So you declare in as a variable in your widget component.
MyWidget({required this.myFunction});
Then when you have to call this component you can write :
...
child : MyWidget(myFunction: (String foo) {},),

auto updating text value Flutter

I'm new to flutter. I'm trying to make a simple automatically updating time.
I tried with RefreshIndicator but it didn't work for me. What is the correct way to make it update per second? Is it possible to make it update with the setState in the bottomNavigationBar by making recursion function?
enter image description here
import 'dart:async';
import 'package:flutter/material.dart';
int Currentindex = 0 ;
late String time1;
var today = DateTime.now();
String time()
{
today = DateTime.now();
time1 = (today.hour.toString()+" : "+today.minute.toString()+" : "+today.second.toString());
return time1;
}
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp( debugShowCheckedModeBanner : false ,
home: Firstpage()
,);
}
}
class Firstpage extends StatefulWidget {
const Firstpage({Key? key}) : super(key: key);
#override
_FirstpageState createState() => _FirstpageState();
}
class _FirstpageState extends State<Firstpage> {
#override
Widget build(BuildContext context) {
return MaterialApp( debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: Currentindex == 0 ? Column(mainAxisAlignment: MainAxisAlignment.center, children: [ElevatedButton(onPressed: (){
setState(() {
Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context)
{
return SecondPage();
}
)
);
});
}, child: Text("Click me"))],
) : Currentindex == 1 ? Column(mainAxisAlignment : MainAxisAlignment.center,children: [Text(time(),style: TextStyle(fontSize: 80),)], ):
SizedBox()
) ,
bottomNavigationBar: BottomNavigationBar(items: const [
BottomNavigationBarItem(label: "Icecream",icon: Icon(Icons.icecream , color: Colors.white,)),
BottomNavigationBarItem(label: "Time",icon: Icon(Icons.access_time , color: Colors.white,))],
backgroundColor: Colors.blue,
onTap: (int index){setState(() {
if(Currentindex == 1){today = DateTime.now();;}
Currentindex = index;
});},
),
),
);
}
}
class SecondPage extends StatefulWidget {
const SecondPage({Key? key}) : super(key: key);
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
#override
Widget build(BuildContext context) {
return MaterialApp( debugShowCheckedModeBanner: false,
home: Scaffold( backgroundColor: Colors.grey,
appBar: AppBar(title: Text("Cool"),backgroundColor: Colors.transparent,),
body: Center(
child: Column(crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ElevatedButton(onPressed: (){
setState(() {
Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context){return Firstpage();
}
)
);
}
);
}, child: Text("Go back"), style: ElevatedButton.styleFrom(primary: Colors.yellow , onPrimary: Colors.orange),)],
),
),
)
);
}
}
See if it helps. Ideally this kind of widget (that updates all the time) should be in leafs and by themselves, to avoid rebuilding parts of your tree unnecessarily.
class MyWidget extends StatefulWidget {
const MyWidget();
#override
State<StatefulWidget> createState() => _MyWidget();
}
class _MyWidget extends State<MyWidget> {
String lastTime = '';
#override
initState() {
super.initState();
timeUpdate.listen((time) => setState(() => lastTime = time ));
}
String get time => DateTime.now().toString();
Stream<String> get timeUpdate async* {
while(true) {
await Future.delayed(Duration(seconds: 1));
yield time;
}
}
#override
Widget build(BuildContext context) {
return Text(
time,
style: Theme.of(context).textTheme.headline4,
);
}
}
If you're trying to make a digital clock looking thing, try this:
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class ClockWidget extends StatelessWidget {
const ClockWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: Stream.periodic(const Duration(seconds: 1)),
builder: (context, snapshot) {
return Text(
DateFormat('HH:mm:ss')
.format(DateTime.now().toLocal())
.toString(),
);
},
);
}
}

update item number in appbar from listview.builder

I am learning flutter and have some experience in javascript.
I want to add length of _suggestions to _appBar.
I know I need setState(), but I can't find the right place to insert setState().
When I add setState in build(), the flutter framework issues error.
I understand the setState requires the build to be called, so if setState() is in build(), the condition is recursive.
And the ListView.builder seems to have no event handler. If there is event handler, I can register setState() there.
// Copyright 2018 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('Welcome to Flutter'),
toolbarHeight: 100.0,
),
body: const Center(
child: RandomWords(),
),
),
);
}
}
/*
https://stackoverflow.com/questions/60902203/flutter-update-the-text-in-appbar
http://fluttersamples.com/
https://bendyworks.com/blog/a-month-of-flutter-rendering-a-list-view-with-stream-builder
https://stackoverflow.com/questions/48481590/how-to-set-update-state-of-statefulwidget-from-other-statefulwidget-in-flutter
*/
class RandomWords extends StatefulWidget {
const RandomWords({Key? key}) : super(key: key);
#override
State<RandomWords> createState() => _RandomWordsState();
}
class _RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
String title = 'Startup Name Generator';
final _appBar = const CustomAppBar();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: _appBar,
body: ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (context, i) {
if(i.isOdd) return const Divider();
final index = i ~/ 2;
if(index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10));
}
return ListTile(
title: Text(
_suggestions[index].asPascalCase,
style: _biggerFont,
),
);
},
),
);
}
}
class CustomAppBar extends StatefulWidget implements PreferredSizeWidget {
const CustomAppBar({Key? key}) : super(key: key);
#override
final Size preferredSize = const Size.fromHeight(56); // default is 56.0
#override
State<CustomAppBar> createState() => _CustomAppBarState();
}
class _CustomAppBarState extends State<CustomAppBar> {
String title = "Title";
_changeTitle(String title) {
setState(() {
this.title = title;
});
}
#override
Widget build(BuildContext context) {
return AppBar(
title: Text(title),
);
}
}
flutter codelab
Tested this code. but not works.
// Copyright 2018 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(
title: const Text('Welcome to Flutter'),
toolbarHeight: 100.0,
),
body: const Center(
child: RandomWords(),
),
),
);
}
}
/*
https://stackoverflow.com/questions/60902203/flutter-update-the-text-in-appbar
http://fluttersamples.com/
https://bendyworks.com/blog/a-month-of-flutter-rendering-a-list-view-with-stream-builder
https://stackoverflow.com/questions/48481590/how-to-set-update-state-of-statefulwidget-from-other-statefulwidget-in-flutter
*/
class RandomWords extends StatefulWidget {
const RandomWords({Key? key}) : super(key: key);
#override
State<RandomWords> createState() => _RandomWordsState();
}
class _RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(totalSuggestions: _suggestions.length.toString()),
body: ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (context, i) {
if(i.isOdd) return const Divider();
final index = i ~/ 2;
if(index >= _suggestions.length) {
setState(()
{
_suggestions.addAll(generateWordPairs().take(10));
});
}
return ListTile(
title: Text(
_suggestions[index].asPascalCase,
style: _biggerFont,
),
);
},
),
);
}
}
class CustomAppBar extends StatefulWidget implements PreferredSizeWidget {
final String totalSuggestions;
const CustomAppBar({Key? key, required this.totalSuggestions}) : super(key: key);
#override
final Size preferredSize = const Size.fromHeight(56); // default is 56.0
#override
State<CustomAppBar> createState() => _CustomAppBarState();
}
class _CustomAppBarState extends State<CustomAppBar> {
#override
Widget build(BuildContext context) {
return AppBar(
title: Row(
children: [
Text("Startup Name Generator"),
Spacer(),
Text(widget.totalSuggestions),
],
),
);
}
}
class RandomWords extends StatefulWidget {
const RandomWords({Key? key}) : super(key: key);
#override
State<RandomWords> createState() => _RandomWordsState();
}
class _RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(totalSuggestions: _suggestions.length);,
body: ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (context, i) {
if(i.isOdd) return const Divider();
final index = i ~/ 2;
if(index >= _suggestions.length) {
setState((){
_suggestions.addAll(generateWordPairs().take(10));
})
}
return ListTile(
title: Text(
_suggestions[index].asPascalCase,
style: _biggerFont,
),
);
},
),
);
}
}
class CustomAppBar extends StatefulWidget implements PreferredSizeWidget {
final String totalSuggestions;
const CustomAppBar({Key? key, requiered this.totalSuggestions}) : super(key: key);
#override
final Size preferredSize = const Size.fromHeight(56); // default is 56.0
#override
State<CustomAppBar> createState() => _CustomAppBarState();
}
class _CustomAppBarState extends State<CustomAppBar> {
#override
Widget build(BuildContext context) {
return AppBar(
title:Row(
chidren: [
Text("Startup Name Generator"),
Spacer(),
Text(widget.totalSuggestions)
],
),
);
}
}
This may help you. Welcome to flutter

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.

Where content of StatefulWidget should be stored?

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