Related
I have an AppBar in main.dart and I want to defined it as primary on it's child, But I want to change the title of AppBar itself when I'm on child's page, how can i do that properly?
void main() => runApp(MyApp());
class MyApp extends StatelessWidget{
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Flutter App",
theme: ThemeData(
primaryColor: Colors.cyan,
brightness: Brightness.dark
),
home: Scaffold(
appBar: AppBar(
title: Text("Main Dart"),
),
body: HomeScreen(),
),
routes: <String, WidgetBuilder>{
'/homeScreen': (buildContext)=>HomeScreen(),
'/second': (buildContext)=>Second()
},
);
}
}
//HomeScreen or Second Widget on different dart file
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
//here I want to change the title of Main Dart to HomeScreen
return Container(
child: Center(
child: FlatButton(
child: new Text("Home screen"),
onPressed: (){
Route route = MaterialPageRoute(builder: (context) => Second());
Navigator.push(context, route);
},
),
),
);
}
}
or I need to put Scaffold(appBar:AppBar(...), ...) in every screen? it is the best approach?
Have a BLoC for app properties in app_properties_bloc.dart
final appBloc = AppPropertiesBloc();
class AppPropertiesBloc{
StreamController<String> _title = StreamController<String>();
Stream<String> get titleStream => _title.stream;
updateTitle(String newTitle){
_title.sink.add(newTitle);
}
dispose() {
_title.close();
}
}
Use stream builder in AppBar like this:
AppBar(
title: StreamBuilder<Object>(
stream: appBloc.titleStream,
initialData: "Main Dart",
builder: (context, snapshot) {
return Text(snapshot.data);
}
),
),
Use this to update title on button's onPressed()
onPressed: () {
appBloc.updateTitle('new title');
},
Just in case you are changing only the title of Scaffold then this will work.
I am creating a DefaultScaffold with the title each screen provides. Here the code will show the MainPage and two other pages which have the same AppBar with changed titles.
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(initialRoute: 'home', routes: <String, WidgetBuilder>{
'home': (context) => SOMain(),
'/secondPage': (context) => DefaultScaffold("Second Screen", SOSecond()),
'/thirdPage': (context) => DefaultScaffold("Third Screen", SOThird()),
});
}
}
class DefaultScaffold extends StatelessWidget {
String title;
Widget body;
DefaultScaffold(this.title, this.body);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: body,
);
}
}
class SOMain extends StatelessWidget {
#override
Widget build(BuildContext context) {
return DefaultScaffold(
"Main Screen",
Center(
child: RaisedButton(
child: Text("Go to second screen"),
onPressed: () {
Navigator.pushNamed(context, '/secondPage');
}),
),
);
}
}
class SOSecond extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text("Go the 3rd screen"),
onPressed: () => Navigator.pushNamed(context, "/thirdPage"),
),
);
}
}
class SOThird extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(child: Text("You are on last screen"));
}
}
Note: This is a simple workaround and may not be the best way to do this.
You can accomplish updating the state of the parent from a child by using a callback function.
Parent Class:
import 'package:flutter/material.dart';
class Parent extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return ParentState();
}
}
class ParentState extends State<Parent> {
String title = "Old Title";
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(title),
),
body: DaysFragmentView(onTitleSelect: (String value) {
setTitle(value);
}
),
);
}
void setTitle(String value) {
setState(() {
title = value;
});
}
}
Child Class
typedef TitleCallback = void Function(Title color);
class DaysFragmentView extends StatelessWidget {
const DaysFragmentView({this.onTitleSelect});
final TitleCallback onTitleSelect;
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
RaisedButton(
child: Text('One'),
onPressed: () {
onTitleSelect("TITLE ONE");
},
),
RaisedButton(
child: Text('Two'),
onPressed: () {
onTitleSelect("TITLE TWO");
},
)
],
);
}
}
Reference:
call-method-in-one-stateful-widget-from-another-stateful-widget-flutter
working-with-callback-in-flutter
Using ValueListenableBuilder is an option.
Use an instance variable
String appTitle;
Then set the app bar as in the following block:
appBar: AppBar(
ValueListenableBuilder<String>(
valueListenable: appTitle,
builder: (context, value, child) {
return Text(appTitle.value);
},
),
After that you can simply set appTitle.value in the other class. The title will be changed too because it listens to that value.
appTitle.value = "Home Screen";
Some answer here are too complicated. Here is a full working example using app bar update from child with scafold widget.
You can run the example in dart pad
import 'package:flutter/material.dart';
void main() {
runApp(const MyHomePage(title: 'init title'));
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final ValueNotifier<String?> _appBarTitleNotifier = ValueNotifier<String?>(null);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: ValueListenableBuilder<String?>(
builder: (BuildContext context, String? value, Widget? child) {
return Text(value ?? widget.title);
},
valueListenable: _appBarTitleNotifier,
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ChildDemoTitleBar(titleNotifier: _appBarTitleNotifier)
],
),
),
),
);
}
}
class ChildDemoTitleBar extends StatefulWidget {
final ValueNotifier<String?> titleNotifier;
const ChildDemoTitleBar({Key? key, required this.titleNotifier})
: super(key: key);
#override
State<ChildDemoTitleBar> createState() => _ChildDemoTitleBarState();
}
class _ChildDemoTitleBarState extends State<ChildDemoTitleBar> {
int _counter = 0;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 20),
child: InkWell(
onTap: () {
_counter++;
widget.titleNotifier.value = "title updated $_counter";
},
child: const Text("tap to update title")));
}
}
I have the following widget which will fit into a different parent widget into its body section. So in the parent widget I call this widget as below
body: MapsDemo(),. The issue now is that at this section I run an interval where every 30 seconds I want to call api to get all the latest markers.
Currently I print the count down as this codes print("${30 - timer.tick * 1}"); My issue is very simple I have 3 three floating action button and I have given them their id. Thus on each count down for the last floatingaction button which is btn3.text I am trying to set its value as ${30 - timer.tick * 1} but it does not work on this basis. How can I update the count down in the button?
class MapsDemo extends StatefulWidget {
#override
State createState() => MapsDemoState();
}
class MapsDemoState extends State<MapsDemo> {
GoogleMapController mapController;
#override
void initState() {
super.initState();
startTimer(30);
}
startTimer(int index) async {
print("Index30");
print(index);
new Timer.periodic(new Duration(seconds: 1), (timer) {
if ((30 / 1) >= timer.tick) {
print("${30 - timer.tick * 1}");
btn3.text = ${30 - timer.tick * 1};
} else {
timer.cancel();
var responseJson = NetworkUtils.getAllMarkers(
authToken
);
mapController.clearMarkers();
//startTimer(index + 1);
}
});
}
//Map<PermissionGroup, PermissionStatus> permissions = await PermissionHandler().requestPermissions([PermissionGroup.contacts]);import 'package:permission_handler/permission_handler.dart';
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
GoogleMap(
onMapCreated: (GoogleMapController controller) {
mapController = controller;
},
initialCameraPosition: new CameraPosition(target: LatLng(3.326411697920109, 102.13127606037108))
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Align(
alignment: Alignment.topRight,
child: Column(
children: <Widget>[
FloatingActionButton(
onPressed: () => print('button pressed'),
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Colors.lightBlue,
child: const Icon(Icons.map, size: 30.0),
heroTag: "btn1",
),
SizedBox(height: 5.0),
FloatingActionButton(
onPressed: () => print('second pressed'),
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Colors.lightBlue,
child: const Icon(Icons.add, size: 28.0),
heroTag: "btn2",
),
SizedBox(height: 5.0),
FloatingActionButton(
onPressed: () => print('second pressed'),
materialTapTargetSize: MaterialTapTargetSize.padded,
backgroundColor: Colors.lightBlue,
child: const Icon(Icons.directions_bike, size: 28.0),
heroTag: "btn3",
),
]
)
),
),
]
)
);
}
}
The below will display a countdown from 30 seconds inside of a FloatingActionButton.
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FAB Countdown Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
static const _defaultSeconds = 30;
Timer _timer;
var _countdownSeconds = 0;
#override
void initState() {
_timer = Timer.periodic(Duration(seconds: 1), (Timer t) => _getTime());
super.initState();
}
#override
void dispose() {
_timer.cancel();
super.dispose();
}
void _getTime() {
setState(() {
if (_countdownSeconds == 0) {
_countdownSeconds = _defaultSeconds;
} else {
_countdownSeconds--;
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomPadding: false,
appBar: AppBar(
title: const Text('FAB Countdown Demo'),
),
floatingActionButton: FloatingActionButton(
child: Text('$_countdownSeconds'), onPressed: () {}),
);
}
}
As title. It since that we can detect the drawer is opened, but is this possible to check it is closed or not? Thanks.
I have added this feature in Flutter 2.0.0. Make sure you are using Flutter SDK version >= 2.0.0 to use this.
Simply use a callback in Scaffold
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
drawer: NavDrawer(),
onDrawerChanged: (isOpen) {
// write your callback implementation here
print('drawer callback isOpen=$isOpen');
},
endDrawer: NavDrawerEnd(),
onEndDrawerChanged: (isOpen) {
// write your callback implementation here
print('end drawer callback isOpen=$isOpen');
},
body:
...
Pull request merged in 2.0.0: https://github.com/flutter/flutter/pull/67249
Happy coding!
Declare a GlobalKey to reference your drawer:
GlobalKey _drawerKey = GlobalKey();
Put the key in your Drawer:
drawer: Drawer(
key: _drawerKey,
Check if your drawer is visible:
final RenderBox box = _drawerKey.currentContext?.findRenderObject();
if (box != null){
//is visible
} else {
//not visible
}
You can copy paste run full code below
You can wrap Drawer with a StatefulWidget and put callback in initState() and dispose()
initState() will call widget.callback(true); means open
dispose() will call widget.callback(false); means close
Slide also work in this case
code snippet
drawer: CustomDrawer(
callback: (isOpen) {
print("isOpen ${isOpen}");
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
_isDrawerOpen = isOpen;
});
});
},
...
class CustomDrawer extends StatefulWidget {
CustomDrawer({
Key key,
this.elevation = 16.0,
this.child,
this.semanticLabel,
this.callback,
}) : assert(elevation != null && elevation >= 0.0),
super(key: key);
final double elevation;
final Widget child;
final String semanticLabel;
final DrawerCallback callback;
#override
_CustomDrawerState createState() => _CustomDrawerState();
}
class _CustomDrawerState extends State<CustomDrawer> {
#override
void initState() {
if (widget.callback != null) {
widget.callback(true);
}
super.initState();
}
#override
void dispose() {
if (widget.callback != null) {
widget.callback(false);
}
super.dispose();
}
#override
Widget build(BuildContext context) {
return Drawer(
key: widget.key,
elevation: widget.elevation,
semanticLabel: widget.semanticLabel,
child: widget.child);
}
}
working demo
full code
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,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
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> {
bool _isDrawerOpen = false;
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
drawer: CustomDrawer(
callback: (isOpen) {
print("isOpen ${isOpen}");
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
_isDrawerOpen = isOpen;
});
});
},
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('Item 1'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
),
ListTile(
title: Text('Item 2'),
onTap: () {
// Update the state of the app.
// ...
},
),
],
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Align(
alignment: Alignment.centerRight,
child: Text(
_isDrawerOpen.toString(),
),
),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class CustomDrawer extends StatefulWidget {
CustomDrawer({
Key key,
this.elevation = 16.0,
this.child,
this.semanticLabel,
this.callback,
}) : assert(elevation != null && elevation >= 0.0),
super(key: key);
final double elevation;
final Widget child;
final String semanticLabel;
final DrawerCallback callback;
#override
_CustomDrawerState createState() => _CustomDrawerState();
}
class _CustomDrawerState extends State<CustomDrawer> {
#override
void initState() {
if (widget.callback != null) {
widget.callback(true);
}
super.initState();
}
#override
void dispose() {
if (widget.callback != null) {
widget.callback(false);
}
super.dispose();
}
#override
Widget build(BuildContext context) {
return Drawer(
key: widget.key,
elevation: widget.elevation,
semanticLabel: widget.semanticLabel,
child: widget.child);
}
}
class SecondRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("route test"),
),
body: Text("SecondRoute"));
}
}
You can simply use onDrawerChanged for detecting if the drawer is opened or closed in the Scaffold widget.
Property :
{void Function(bool)? onDrawerChanged}
Type: void Function(bool)?
Optional callback that is called when the Scaffold.drawer is opened or closed.
Example :
#override Widget build(BuildContext context) {
return Scaffold(
onDrawerChanged:(val){
if(val){
setState(() {
//foo bar;
});
}else{
setState(() {
//foo bar;
});
}
},
drawer: Drawer(
child: Container(
)
));
}
When you click a Drawer Item where you will navigate to a new screen, there in the Navigator.push(..) call, you can add a .then(..) clause, and then know when the Drawer item Screen has been popped.
Here is the ListTile for a Drawer item which makes the Navigator.push(..) call when clicked , and the the associated .then(..) callback block:
ListTile(
title: Text('About App'),
onTap: () {
Navigator.push(
_ctxt,
MaterialPageRoute(builder: (context) => AboutScreen()),
).then(
(value) {
print('Drawer callback for About selection');
if (_onReadyCallback != null) {
_onReadyCallback();
}
},
);
}),
_onReadyCallback() represents a Function param you can pass in.
I found this is approach - of leveraging the .then() callback from a .push() call - to be a very useful concept to understand with Flutter in general.
Big thanks to the main 2 answers here:
Force Flutter navigator to reload state when popping
Here's the complete Drawer code:
Drawer drawer = Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
decoration: BoxDecoration(
color: Color(0xFF7FAD5F),
),
child: Text(App.NAME_MENU),
),
ListTile(
title: Text('About App'),
onTap: () {
Navigator.push(
_ctxt,
MaterialPageRoute(builder: (context) => AboutScreen()),
).then(
(value) {
print('Drawer callback for About selection');
if (_onReadyCallback != null) {
_onReadyCallback();
}
},
);
}),
],
),
);
I would recommend that you use this package : https://pub.dev/packages/visibility_detector.
Afterwards you should assign a GlobalKey, like _drawerKey for instance, to the Drawer widget, after which you would be able to detect when the drawer is closed like this:
VisibilityDetector(
key: _drawerKey,
child: Container(),
onVisibilityChanged: (info) {
if (info.visibleFraction == 0.0) {
// drawer not visible.
}
},
)
I'm trying to create a Radio in a showDialog, however the animation that occurs on Radio does not appear in showDialog.
For example: when tapped in foo2 nothing happens, and when you exit in showDialog and go back to it, foo2 is selected.
Below is the code and a gif showing what is happening:
import "package:flutter/material.dart";
void main() {
runApp(new ControlleApp());
}
class ControlleApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: "My App",
home: new HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
HomePageState createState() => new HomePageState();
}
enum _RadioGroup {
foo1,
foo2
}
class HomePageState extends State<HomePage> {
_RadioGroup _itemType = _RadioGroup.foo1;
void changeItemType(_RadioGroup type) {
setState(() {
_itemType = type;
});
}
void showDemoDialog<T>({ BuildContext context, Widget child }) {
showDialog<T>(
context: context,
child: child,
);
}
#override
Widget build(BuildContext context){
return new Scaffold(
appBar: new AppBar(backgroundColor: new Color(0xFF26C6DA)),
body: new Container(
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new InkWell(
onTap: (){
showDemoDialog<String>(
context: context,
child: new SimpleDialog(
title: const Text("show"),
children: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Radio<_RadioGroup>(
groupValue: _itemType,
value: _RadioGroup.foo1,
onChanged: changeItemType
),
const Text("foo1"),
new Radio<_RadioGroup>(
groupValue: _itemType,
value: _RadioGroup.foo2,
onChanged: changeItemType
),
const Text("foo2"),
],
)
],
)
);
},
child: new Container(
margin: new EdgeInsets.only(top: 16.0, bottom: 8.0),
child: new Text("Show"),
),
)
],
),
)
);
}
}
Remember that components are immutable.
When you call showDialog, the content of that dialog won't change even if HomePage does.
The solution is easy. You need to refactor a bit your code to something like :
showDialog(
context: context,
builder: (context) => MyForm()
)
and instead of changing the state of HomePage, you instead change the state of MyForm.
example :
class Test extends StatelessWidget {
void onSubmit(String result) {
print(result);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
onPressed: () => showDialog(context: context, builder: (context) => MyForm(onSubmit: onSubmit)),
child: Text("dialog"),
),
),
);
}
}
typedef void MyFormCallback(String result);
class MyForm extends StatefulWidget {
final MyFormCallback onSubmit;
MyForm({this.onSubmit});
#override
_MyFormState createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
String value = "foo";
#override
Widget build(BuildContext context) {
return SimpleDialog(
title: Text("My form"),
children: <Widget>[
Radio(
groupValue: value,
onChanged: (value) => setState(() => this.value = value),
value: "foo",
),
Radio(
groupValue: value,
onChanged: (value) => setState(() => this.value = value),
value: "bar",
),
FlatButton(
onPressed: () {
Navigator.pop(context);
widget.onSubmit(value);
},
child: new Text("submit"),
)
],
);
}
}
How could I make the name() function run whenever the Page1 page appeared?
In the code below before going to Page2 I execute the dispose()
Already inside Page2 if I click the back button or the physical button of Android the function name() is not executed, but if I click the 'go to Page1' button the function name() is executed.
Could you help me to always execute the name() function when Page1 appears?
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
routes: <String, WidgetBuilder> {
'/page2': (BuildContext context) => new Page2(),
},
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String nameScreen;
String name() {
return 'foo1';
}
#override
void initState() {
super.initState();
this.nameScreen = name();
}
#override
void dispose() {
this.nameScreen = '';
super.dispose();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Page 1'),
backgroundColor: new Color(0xFF26C6DA),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new RaisedButton(
child: const Text('go to Page2'),
onPressed: () async {
dispose();
bool isLoggedIn = await Navigator.of(context).pushNamed('/page2');
if (isLoggedIn) {
setState((){
this.nameScreen = name();
});
}
},
),
new Text(
'$nameScreen',
),
],
),
),
);
}
}
class Page2 extends StatelessWidget{
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Page 2'),
backgroundColor: new Color(0xFFE57373)
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new RaisedButton(
child: const Text('go back to Page1'),
onPressed: () {
Navigator.pop(context, true);
}
),
],
),
),
);
}
}
There is no need to call dispose at all when you are willing to pop and change State later, since dispose will remove the current object from the tree, which does not translate to the logic you are trying to develop.
You can indeed override the BackButton and pass the same call of Navigator.pop(context, result) to it. Check the following example I have tweaked your code a little bit to show you the difference between each State of your nameScreen field. I hope this helps you.
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String nameScreen = "";
String name() {
return 'foo1';
}
#override
void initState() {
super.initState();
this.nameScreen = "From initState";
}
#override
void dipose(){
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Page 1'),
backgroundColor: Color(0xFF26C6DA),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: const Text('go to Page2'),
onPressed: () async {
//dispose(); ///No need for dispose
String result = await Navigator.of(context).pushNamed('/page2');
setState((){
this.nameScreen = result;
});
},
),
Text(
'$nameScreen',
),
],
),
),
);
}
}
class Page2 extends StatelessWidget{
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: ()async{
Navigator.pop(context,"From BackButton");
}),
title: const Text('Page 2'),
backgroundColor: Color(0xFFE57373)
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: const Text('go back to Page1'),
onPressed: () {
Navigator.pop(context, "From RaisedButton");
}
),
],
),
),
);
}
One way of doing this is to use the .whenComplete() method on the Navigator widget.
Suppose you are going to the second page from the first page. Here you have to pass the functionThatSetsTheState as a pointer to the navigation part of your code.
The function looks like this and should be in a Stateful Widget.
void functionThatSetsTheState(){
setState(() {});
}
Your navigation code for OnPressed, OnTap, OnLongPress, etc.
Navigator.of(context)
.push(
MaterialPageRoute(builder: (BuildContext context) => SecondPage()))
.whenComplete(() => {functionThatSetsTheState()});
You can override the back button on the second screen. And instead of system closing, do
WillPopScope(
onWillPop: () {
print('back pressed');
Navigator.pop(context, "From BackButton");
return true;
},
child: Scaffold(...)
You can use RouteObserves if you want to execute some function whenever your page appears, you will have to implement RouteAware on the page where you want to run execute the function whenever the screens appears, you're gonna have to do something like this on ur Page1
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>(); // add this on your main class
void main() {
runApp(MaterialApp(
home: Container(),
navigatorObservers: [routeObserver], // add observer here;
));
}
// your page where func should run whenever this page appears
class MyHomePage extends StatefulWidget with RouteAware {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String nameScreen = "";
String name() {
return 'foo1';
}
#override
void initState() {
super.initState();
this.nameScreen = "From initState";
}
#override
void didChangeDependencies() {
super.didChangeDependencies();
routeObserver.subscribe(this, ModalRoute.of(context));
}
#override
void dispose() {
routeObserver.unsubscribe(this);
super.dispose();
}
// implementing RouteAware method
void didPush() {
// Route was pushed onto navigator and is now topmost route.
name(); // your func goes here
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Page 1'),
backgroundColor: Color(0xFF26C6DA),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: const Text('go to Page2'),
onPressed: () async {
//dispose(); ///No need for dispose
String result = await Navigator.of(context).pushNamed('/page2');
setState((){
this.nameScreen = result;
});
},
),
Text(
'$nameScreen',
),
],
),
),
);
}
}
you can head over to this link for more explanation
https://api.flutter.dev/flutter/widgets/RouteObserver-class.html
Say you want to navigate from page 1 to page 2 and immediately after page 2 loads execute a function in page 2 (useful for showing a dialog immediately when page 2 loads) :
You can do this by adding in initState or didChangeDependencies of page 2 :
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
// Function to execute
});
If you want to add some logic to put a condition before executing the function, simply push an argument in your page 1 :
Navigator.of(context).pushNamed("/page-2", arguments : true)
Finnaly the code in page 2 becomes:
_functionToExecute(){
print("done");
}
#override
void didChangeDependencies() {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
if(ModalRoute.of(context).settings.arguments)
_functionToExecute()
});
}