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")));
}
}
How do you navigate to a new screen in Flutter?
These questions are similar, but are asking more than I am.
Flutter - Navigate to a new screen, and clear all the previous screens
Flutter: How do I navigate to a new screen using DropDownMenuItems
Flutter: Move to a new screen without back
flutter navigation to new screen not working
I am adding an answer below.
Navigate to a new screen:
Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewScreen()));
where context is the BuildContext of a widget and NewScreen is the name of the second widget layout.
Code
main.dart
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: HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home Screen')),
body: Center(
child: ElevatedButton(
child: const Text(
'Navigate to a new screen >>',
style: TextStyle(fontSize: 24.0),
),
onPressed: () {
_navigateToNextScreen(context);
},
),
),
);
}
void _navigateToNextScreen(BuildContext context) {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewScreen()));
}
}
class NewScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('New Screen')),
body: const Center(
child: Text(
'This is a new screen',
style: TextStyle(fontSize: 24.0),
),
),
);
}
}
See also
Documentation
Navigator and Routes and Transitions... Oh, My! - Simon Lightfoot | Flutter Europe
To load new screens with Flutter pre-canned animations, use their respective transition classes. For example:
Container Transformation
Basically we have the first widget or screen transform into the next screen. For this we need to use OpenContainer. The following code illustrates an item in a ListView transformed to its details page.
#override
Widget build(BuildContext context) {
return Card(
color: Colors.white,
elevation: 2.0,
child: OpenContainer(
transitionType: ContainerTransitionType.fadeThrough,
closedColor: Theme.of(context).cardColor,
closedElevation: 0.0,
openElevation: 4.0,
transitionDuration: Duration(milliseconds: 1500),
openBuilder: (BuildContext context, VoidCallback _) => THENEXTSCREEN(),
closedBuilder: (BuildContext _, VoidCallback openContainer) {
return ListTile(
leading: Icon(Icons.album),
title: Text("ITEM NAME"),
);
},
),
);
}
Shared Axis
This transition is similar to that in Tab or Stepper. We need SharedAxisTransition, PageTransitionSwitcher, along with a state to model transition between active and previous page. If we only switch between two pages we can use a simple boolean isFirstPage for it. Here's the snippet with Provider as state management:
#override
Widget build(BuildContext context) {
return Consumer<YourState>(
builder: (context, state, child) {
return PageTransitionSwitcher(
duration: const Duration(milliseconds: 1500),
reverse: !state.isFirstPage, // STATE
transitionBuilder: (
Widget child,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return SharedAxisTransition(
child: child,
animation: animation,
secondaryAnimation: secondaryAnimation,
transitionType: SharedAxisTransitionType.horizontal,
);
},
child: state.isFirstPage? FIRSTPAGE() : SECONDPAGE(), // STATE
);
},
);
}
Note that in all these scenarios we don't use Navigator and MaterialPageRoute. All these codes are derived from animations repo so you may want to check it out first.
Navigate to next screen with back using Navigator.push()
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),);
Navigate to next screen without back using Navigator.pushReplacement()
Navigator.pushReplacement(
context,MaterialPageRoute(builder: (context) => SecondRoute()),);
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => NextScreenName()));
}
If you are familiar with web development this approach is similar to routing.
main.dart
void main() {
setupLocator();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
routes: {
'/' : (BuildContext context)=>HomePage(),
'/register' : (BuildContext context)=>RegisterPage(),
},
);
}
}
You can add button onPressed event from the homepage.dart to navigate register.dart as follows.
onPressed: (){
Navigator.pushReplacementNamed(context, '/register');
},
Here is a full example of routes push / pop:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Routes',
routes: {
'/login': (BuildContext context) => Login(),
// add another route here
// '/register': (BuildContext context) => Register(),
},
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Routes'),
);
}
}
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 Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
RaisedButton(
onPressed: () {
// This gives the back button:
Navigator.of(context).pushNamed('/login');
// This doesn't give the back button (it replaces)
//Navigator.pushReplacementNamed(context, '/login');
},
child: Text('Login'),
),
],
),
),
);
}
}
class Login extends StatefulWidget {
#override
_LoginState createState() => _LoginState();
}
class _LoginState extends State<Login> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Login Page'),
),
body: Center(
child: RaisedButton(
onPressed: () {
// This will only work for pushNamed
Navigator.of(context).pop();
},
child: Text('Go back'),
),
));
}
}
you can use that way in your build widget
onTap: () { Navigator.of(context).push(MaterialPageRoute( builder: (context) => NewScreen()));},
In formal method :
Navigator.push(context, MaterialPageRoute(builder: (context)=>Second()));
In GetX method :
Get.to(Second());
If we can navigate screen into another page and delete current page from stack then we can use method which is define below :
Get.off(Third());
If we can navigate screen into another page and delete all route or page from stack then we can use the method which is define below :
Get.offAll(Third());
If we want to use Navigator.pop() then GetX give a Method which is define below :
Get.back();
You can try with the following code
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => YourNextScreen())),
I found a good tutorial that I have followed along, it is very comprehensive with screenshots and step by step, you can also download the code and just run it. Very helpful for me learning Flutter especially I am totally a begineer.
https://medium.com/#misterflutter/lesson-5-creating-new-screens-f740994190c7
https://medium.com/#misterflutter/lesson-6-creating-new-screens-part-2-4997085a43af?sk=d2a0fb723af42b78800f7cf19b312b62
With the Get plugin, you can navigate to a new page by simply calling
Get.to(Page());
This way you can present the next screen
Navigator.of(context).push(
MaterialPageRoute(fullscreenDialog: true,
builder: (context) => const NewScreen(),
),
);
FloatingActionButton(
onPressed: (){
Navigator.of(context).push(MaterialPageRoute(builder: (context) => const AddUser()));
},
child: const Icon(Icons.add),
),
I'm trying to start a new screen within an onTap but I get the following error:
Navigator operation requested with a context that does not include a
Navigator.
The code I am using to navigate is:
onTap: () { Navigator.of(context).pushNamed('/settings'); },
I have set up a route in my app as follows:
routes: <String, WidgetBuilder>{
'/settings': (BuildContext context) => new SettingsPage(),
},
I've tried to copy the code using the stocks sample application. I've looked at the Navigator and Route documentation and can't figure out how the context can be made to include a Navigator. The context being used in the onTap is referenced from the parameter passed into the build method:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
SettingsPage is a class as follows:
class SettingsPage extends Navigator {
Widget buildAppBar(BuildContext context) {
return new AppBar(
title: const Text('Settings')
);
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: buildAppBar(context),
);
}
}
TLDR: Wrap the widget which needs to access to Navigator into a Builder or extract that sub-tree into a class. And use the new BuildContext to access Navigator.
This error is unrelated to the destination. It happens because you used a context that doesn't contain a Navigator instance as parent.
How do I create a Navigator instance then ?
This is usually done by inserting in your widget tree a MaterialApp or WidgetsApp. Although you can do it manually by using Navigator directly but less recommended. Then, all children of such widget can access NavigatorState using Navigator.of(context).
Wait, I already have a MaterialApp/WidgetsApp !
That's most likely the case. But this error can still happens when you use a context that is a parent of MaterialApp/WidgetsApp.
This happens because when you do Navigator.of(context), it will start from the widget associated to the context used. And then go upward in the widget tree until it either find a Navigator or there's no more widget.
In the first case, everything is fine. In the second, it throws a
Navigator operation requested with a context that does not include a Navigator.
So, how do I fix it ?
First, let's reproduce this error :
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Center(
child: RaisedButton(
child: Text("Foo"),
onPressed: () => Navigator.pushNamed(context, "/"),
),
),
);
}
}
This example creates a button that attempts to go to '/' on click but will instead throw an exception.
Notice here that in the
onPressed: () => Navigator.pushNamed(context, "/"),
we used context passed by to build of MyApp.
The problem is, MyApp is actually a parent of MaterialApp. As it's the widget who instantiate MaterialApp! Therefore MyApp's BuildContext doesn't have a MaterialApp as parent!
To solve this problem, we need to use a different context.
In this situation, the easiest solution is to introduce a new widget as child of MaterialApp. And then use that widget's context to do the Navigator call.
There are a few ways to achieve this. You can extract home into a custom class :
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHome()
);
}
}
class MyHome extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text("Foo"),
onPressed: () => Navigator.pushNamed(context, "/"),
),
);
}
}
Or you can use Builder :
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Builder(
builder: (context) => Center(
child: RaisedButton(
child: Text("Foo"),
onPressed: () => Navigator.pushNamed(context, "/"),
),
),
),
);
}
}
Hy guys, i have the same problem. This is occur for me. The solution what i found is very simple. Only what i did is in a simple code:
void main() {
runApp(MaterialApp(
home: YOURAPP() ,
),
);
}
I hope was useful.
Make sure your current parent widget not with same level with MaterialApp
Wrong Way
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Title'),
),
body: Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
child: RaisedButton(
onPressed: () {
//wrong way: use context in same level tree with MaterialApp
Navigator.push(context,
MaterialPageRoute(builder: (context) => ScanScreen()));
},
child: const Text('SCAN')),
)),
),
);
}
}
Right way
void main() => runApp(MaterialApp(
title: "App",
home: HomeScreen(),
));
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('Title'),
),
body: Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
child: RaisedButton(
onPressed: () {
//right way: use context in below level tree with MaterialApp
Navigator.push(context,
MaterialPageRoute(builder: (context) => ScanScreen()));
},
child: const Text('SCAN')),
)),
);
}
}
Just like with a Scaffold you can use a GlobalKey. It doesn't need context.
final _navKey = GlobalKey<NavigatorState>();
void _navigateToLogin() {
_navKey.currentState.popUntil((r) => r.isFirst);
_navKey.currentState.pushReplacementNamed(LoginRoute.name);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: _navKey,
...
);
}
I set up this simple example for routing in a flutter app:
import 'package:flutter/material.dart';
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new MyHomePage(),
routes: <String, WidgetBuilder>{
'/settings': (BuildContext context) => new SettingsPage(),
},
);
}
}
class MyHomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('TestProject'),
),
body: new Center(
child: new FlatButton(
child: const Text('Go to Settings'),
onPressed: () => Navigator.of(context).pushNamed('/settings')
)
)
);
}
}
class SettingsPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('SettingsPage'),
),
body: new Center(
child: new Text('Settings')
)
);
}
}
Note, that the SettingsPage extends StatelessWidget and not Navigator. I'm not able to reproduce your error.
Does this example help you in building your app? Let me know if I can help you with anything else.
You should rewrite your code in main.dart
FROM:
void main() => runApp(MyApp());
TO
void main() {
runApp(MaterialApp(
title: 'Your title',
home: MyApp(),));}
The point is to have the home property to be your first page
this worked for me, I hope it will help someone in the future
A complete and tested solution:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:my-app/view/main-view.dart';
class SplashView extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: Builder(
builder: (context) => new _SplashContent(),
),
routes: <String, WidgetBuilder>{
'/main': (BuildContext context) => new MainView()}
);
}
}
class _SplashContent extends StatefulWidget{
#override
_SplashContentState createState() => new _SplashContentState();
}
class _SplashContentState extends State<_SplashContent>
with SingleTickerProviderStateMixin {
var _iconAnimationController;
var _iconAnimation;
startTimeout() async {
var duration = const Duration(seconds: 3);
return new Timer(duration, handleTimeout);
}
void handleTimeout() {
Navigator.pushReplacementNamed(context, "/main");
}
#override
void initState() {
super.initState();
_iconAnimationController = new AnimationController(
vsync: this, duration: new Duration(milliseconds: 2000));
_iconAnimation = new CurvedAnimation(
parent: _iconAnimationController, curve: Curves.easeIn);
_iconAnimation.addListener(() => this.setState(() {}));
_iconAnimationController.forward();
startTimeout();
}
#override
Widget build(BuildContext context) {
return new Center(
child: new Image(
image: new AssetImage("images/logo.png"),
width: _iconAnimation.value * 100,
height: _iconAnimation.value * 100,
)
);
}
}
As per this comment If your navigator is inside Material context navigator push will give this error. if you create a new widget and assign it to the material app home navigator will work.
This won't work
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text("Title"),
),
body: new Center(child: new Text("Click Me")),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.add),
backgroundColor: Colors.orange,
onPressed: () {
print("Clicked");
Navigator.push(
context,
new MaterialPageRoute(builder: (context) => new AddTaskScreen()),
);
},
),
),
);
}
}
This will work
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new HomeScreen());
}
}
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Title"),
),
body: new Center(child: new Text("Click Me")),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.add),
backgroundColor: Colors.orange,
onPressed: () {
print("Clicked");
Navigator.push(
context,
new MaterialPageRoute(builder: (context) => new AddTaskScreen()),
);
},
),
);
}
}
I was facing the same problem and solved by removing home from MaterialApp and use initialRoute instead.
return MaterialApp(
debugShowCheckedModeBanner: false,
initialRoute: '/',
routes: {
'/': (context) => MyApp(),
'/settings': (context) => SettingsPage(),
},
);
And
onTap: () => {
Navigator.pushNamed(context, "/settings")
},
It is Simple
instead using this normal code
`runApp(BasicBankingSystem());`
wrap it with MaterialApp
runApp(MaterialApp(home: BasicBankingSystem()));
It happens because the context on the widget that tries to navigate is still using the material widget.
The short answer for the solution is to :
extract your widget
that has navigation to new class so it has a different context when calling the navigation
When your screen is not navigated from other screen,you don't initially have access to the navigator,Because it is not instantiated yet.So in that case wrap your widget with builder and extract context from there.This worked for me.
builder: (context) => Center(
child: RaisedButton(
child: Text("Foo"),
onPressed: () => Navigator.pushNamed(context, "/"),
),
You ca use this plugin
https://pub.dev/packages/get/versions/2.0.2
in The MaterialApp assign property navigatorKey: Get.key,
MaterialApp(
navigatorKey: Get.key,
initialRoute: "/",
);
you can access Get.toNamed("Your route name");
Change your main function example:
void main() {
runApp(
MaterialApp(
title: 'Your title',
home: MyApp(),
)
);
}
use this
void main() {
runApp(MaterialApp(debugShowCheckedModeBanner: false, home: MyApp()),);
}
instead of this
void main() {runApp(MyApp());}
Wrap with materialapp
reproduce code
import 'dart:convert';
import 'package:flutter/material.dart';
void main() {
// reproduce code
runApp(MyApp());
// working switch //
// runApp(
//
// MaterialApp(debugShowCheckedModeBanner: false, home: MyApp()),);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body:
Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 100,
width: 100,
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => IntroPage(Isscar4: true)),
);
},
child: RichText(
text: TextSpan(
text: 'CAR',
style: TextStyle(
letterSpacing: 3,
color: Colors.white,
fontWeight: FontWeight.w400),
children: [
TextSpan(
text: '4',
style: TextStyle(
fontSize: 25,
color: Colors.red,
fontWeight: FontWeight.bold))
],
)),
),
),
],
),
SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 100,
width: 100,
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => IntroPage(Isscar4: false)),
);
},
child: RichText(
text: TextSpan(
text: 'BIKE',
style: TextStyle(
letterSpacing: 3,
color: Colors.white,
fontWeight: FontWeight.w400),
children: [
TextSpan(
text: '2',
style: TextStyle(
fontSize: 25,
color: Colors.red,
fontWeight: FontWeight.bold))
],
)),
),
),
],
)
])));
}
MaterialApp Swithwidget(istrue) {
return MaterialApp(
home: Scaffold(
body: IntroPage(
Isscar4: istrue,
),
),
);
}
}
class Hi extends StatelessWidget {
const Hi({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: Text("df"),
);
}
}
class IntroPage extends StatelessWidget {
final Isscar4;
IntroPage({
Key? key,
required this.Isscar4,
}) : super(key: key);
List<Widget> listPagesViewModel = [];
List<IntroModel> models = [];
#override
Widget build(BuildContext context) {
List<dynamic> intro = fetchIntroApi(Isscar4);
intro.forEach((element) {
var element2 = element as Map<String, dynamic>;
var cd = IntroModel.fromJson(element2);
models.add(cd);
});
models.forEach((element) {
listPagesViewModel.add(Text(""));
});
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Container(),
));
}
List fetchIntroApi(bool bool) {
var four = bool;
if (four) {
var data =
'[ {"name_Title": "title name1","description": "description1"}, {"name_Title": "title name2","description": "description2"}, {"name_Title": "title name3","description": "description3"}, {"name_Title": "title name4","description": "description4"} ]';
return json.decode(data);
} else {
var data =
'[ {"name_Title": "title name","description": "description1"}, {"name_Title": "title name2","description": "description2"}, {"name_Title": "title name3","description": "description3"} ]';
return json.decode(data);
}
}
}
class IntroModel {
String? nameTitle;
String? description;
IntroModel({this.nameTitle, this.description});
IntroModel.fromJson(Map<String, dynamic> json) {
nameTitle = json['name_Title'];
description = json['description'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['name_Title'] = this.nameTitle;
data['description'] = this.description;
return data;
}
}
class Splash extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Splash Screen',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: MyState(),
debugShowCheckedModeBanner: false,
);
}
void main() {
runApp(Splash());
}
class MyState extends StatefulWidget{
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyState> {
#override
void initState() {
super.initState();
Timer(Duration(seconds: 3),
()=>Navigator.pushReplacement(context,
MaterialPageRoute(builder:
(context) =>
Login()
)
)
);
}
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center ,
children: [
Container(
child:
Image.asset("assets/images/herosplash.png"),
),
],
),
);
}
}
Builder(
builder: (context) {
return TextButton(
child: const Text('Bearbeiten'),
onPressed:(){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const gotothesiteyouwant()),
);
});
}
),
Here, all you need is to make MaterialApp the parent of your Build. This is because the context that you've used to navigate to a different screen is finding a MaterialApp or a WidgetApp as a parent of the build.
And Since in your case, the situation is the opposite, therefore you need to modify it by either calling a new Stateless widget the parent of is the MaterialApp or by simply using a Builder as home: Builder in MaterialApp.
Hope this would help!
I am new to Flutter and just started to make a tiny little app which takes a list of Top Movies from a server using an async request. and when I tap on top of each one of list items, then it navigates me to another screen to show some details about the movie.
But there is a problem, when I tap on any item to see it's details, inside the details page, when I press back, in the first page, it just loads data again which is not a good user experience. also uses more battery and bandwidth for each request.
I don't know if this is a natural behavior of Flutter to lose data of a Stateful widget after navigating to another screen or there is something wrong with my code.
Can anybody help me with this
This is my code:
import "package:flutter/material.dart";
import "dart:async";
import "dart:convert";
import "package:http/http.dart" as http;
void main() {
runApp(MovieApp());
}
class MovieApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'test',
home: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text("Top Movies List",
textDirection: TextDirection.rtl,
style: TextStyle(color: Colors.black87))
]
)
),
body: MoviesList()
)
);
}
}
class MoviesList extends StatefulWidget {
#override
MoviesListState createState() => new MoviesListState();
}
class MoviesListState extends State<MoviesList> {
List moviesList = [];
Future<Map> getData() async {
http.Response response = await http.get(
'http://api.themoviedb.org/3/discover/movie?api_key={api_key}'
);
setState(() {
moviesList = json.decode(response.body)['results'];
});
// return json.decode(response.body);
}
#override
Widget build(BuildContext context) {
getData();
if(moviesList == null) {
return Scaffold(
body: Text('Getting data from server')
);
} else {
return ListView.builder(
itemCount: moviesList.length,
itemBuilder: (context, index){
return Container(
child: ListTile(
title: Text(moviesList[index]['title']),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MovieDetails()),
);
}
)
);
}
);
}
}
}
class MovieDetails extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Details')
),
body: Container(
child: Center(
child: RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Go back!'),
),
)
),
);
}
}
Move your getData() method inside the initState() in your State class.
(Remove it from build method)
#override
void initState() {
getData();
super.initState();
}
I'm trying to learn Dart/Flutter and am working on an example where there's a button on the app that says "Get Data", and when I touch it I want to retrieve JSON data from a restful service.
I see the web service being called in fetchPost, but the builder property of the FutureBuilder isn't called.
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'ResultsList.dart';
import 'dart:convert';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Restul Test',
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> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: (){
FutureBuilder<ResultsList>(
future: fetchPost(),
builder: (context, snapshot){
print('In Builder');
}
);
},
child: Text('Get data'),
)
],
),
)
);
}
}
Future<ResultsList> fetchPost() async {
final response = await http.get('http://mywebserviceurl');
if (response.statusCode == 200){
print('Received data');
return ResultsList.fromJson(json.decode(response.body));
}
else {
throw Exception('Failed to load data');
}
}
Interestingly though, if I move the FutureBuilder out of the onPressed of the button to the child of Center, I do see the builder property getting called.
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'ResultsList.dart';
import 'dart:convert';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Restul Test',
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> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: FutureBuilder<ResultsList>(
future: fetchPost(),
builder: (context, snapshot){
print ('In Builder');
return Container();
}
)
)
);
}
}
Future<ResultsList> fetchPost() async {
final response = await http.get('http://mywebserviceurl');
if (response.statusCode == 200){
print('Received data');
return ResultsList.fromJson(json.decode(response.body));
}
else {
throw Exception('Failed to load data');
}
}
Obviously I'm missing something, but any idea what I'm doing wrong?
If you want to get some data from request - you don't need FutureBuilder. You can do:
RaisedButton(
onPressed: (){
fetchPost().then((result) {
print('In Builder');
})
},
child: Text('Get data'),
)
or
RaisedButton(
onPressed: () async {
var result = await fetchPost()
print('In Builder');
},
child: Text('Get data'),
)
The onPressed method in this RaisedButton is actually not doing anything. It just creates a new FutureBuilder which does nothing but existing^^ It's like you would just call 1+1;, which just creates a value, but that value is not used to do anything.
RaisedButton(
onPressed: (){
FutureBuilder<ResultsList>(
future: fetchPost(),
builder: (context, snapshot){
print('In Builder');
}
);
},
child: Text('Get data'),
)
You could have body be assigned to a Widget(which could just be called body or whatever you want^^), which you then change in a setState((){body = FutureBuilder(/*...*/}); call.
For me FutureBuilder not working in onPresses...
I used this way :
I defined a variable in state:
bool visiblity = false;
and I used this code in build:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () {
visiblity=true;
fetchPost();
},
child: Text('Get data'),
),
FutureBuilder<ResultsList>(
future: ("Your View Model that return from call back"),
builder: (context, snapshot) {
if (visiblity) {
print('In Builder');
visiblity=false;
} else
return Container();
}
),
],
),
)
);
}
I didn't put FutureBuilder in onPressed. I put that in body and changed visibility after return result.