I wanted to have persistent bottom navigation bar across my whole app so after searching for couple of hours I found a solution.
I was inspired from this blog post and wrote my solution code Flutter — navigating off the charts
import 'package:flutter/material.dart';
import './login/login.dart';
import './alerts/alerts.dart';
import './home/home.dart';
import './Theme.dart';
import './settings/settings.dart';
import './enroll/enroll.dart';
import './add_device/add_device.dart';
import './eachDevice/index.dart';
import './device_settings/device_settings.dart';
import 'splash_screen/splash_screen.dart';
import './geofences/geofence_list.dart';
import './geofences/draw_geofence.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import './home/second_navigation_bar.dart';
import 'dart:io';
import 'package:path/path.dart';
void main() {
GlobalKey<NavigatorState> navigator = new GlobalKey<NavigatorState>();
HttpOverrides.global = new AppHttpOverrides();
Map<String, WidgetBuilder> _routes = <String, WidgetBuilder>{
"/alerts": (BuildContext context) => new Alerts(),
"/login": (BuildContext context) => new LoginPage(),
"/settings": (BuildContext context) => new Settings(),
"/enroll": (BuildContext context) => new Enroll(),
"/add_device": (BuildContext context) => new AddDevice(),
"/history": (BuildContext context) => new History(),
"/home": (BuildContext context) => new Home(),
"/device_settings": (BuildContext context) => new DeviceSettings(),
"/geofence_list": (BuildContext context) => new GeofenceList(),
"/draw_geofence": (BuildContext context) => new DrawGeofence(),
};
runApp(new MaterialApp(
navigatorKey: navigator,
home: new SplashScreen(),
builder: (context, child) {
return new Scaffold(
body: child,
bottomNavigationBar:myBottomNavigationBar(),
resizeToAvoidBottomPadding: false
);
},
theme: buildTheme(),
routes: _routes,
));
}
This code works perfectly and I have static bottom navigation bar in all app pages however I want to exclude bottom navigation bar in some routes like login page how can I exclude bottom navigation bar for some specific pages with this approach.
Navigator.of(context, rootNavigator: true).pushReplacement(MaterialPageRoute(builder: (context) => new LoginActivity()));
Take a variable bool isBottomNavBarToBeShown. You can use some kind of function for the body in Scaffold like
_getScreen(route) {
switch (route) {
case 'route1':
return Route1();
break;
case 'route2':
return Route2();
break;
default:
break;
}
}
but in your case, you have to change
"/login": (BuildContext context) => new LoginPage(),
to
"/login": (BuildContext context) {
return new LoginPage();
},
now just set
setState(() {
isBottomNavBarToBeShown=false;
});
e.g.
case 'route1':
setState(() {
isBottomNavBarToBeShown=false;
});
return Route1();
break;
in your case
"/login": (BuildContext context) {
setState(() {
isBottomNavBarToBeShown=false;
});
return new LoginPage();
},
so in your Scaffold
bottomNavigationBar:myBottomNavigationBar(),
just use bottomNavigationBar:isBottomNavBarToBeShown ? myBottomNavigationBar() : null,
First, create a Stateful screen first and add this in Scaffold.
So you can access setState
Please update on this, if it works for you.
Declare variable for bottomNavigationBar content like as
var navContent;
Create method for the exclude your bottomNavigationBar
excludeBottomNavigationBar(){
return Container(
height: 0.0,
);
}
Now, you need to assign the bottomNavigationBar content according your requirement, exclude bottomNavigationBar for the login page
import 'package:flutter/material.dart';
import './login/login.dart';
import './alerts/alerts.dart';
import './home/home.dart';
import './Theme.dart';
import './settings/settings.dart';
import './enroll/enroll.dart';
import './add_device/add_device.dart';
import './eachDevice/index.dart';
import './device_settings/device_settings.dart';
import 'splash_screen/splash_screen.dart';
import './geofences/geofence_list.dart';
import './geofences/draw_geofence.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import './home/second_navigation_bar.dart';
import 'dart:io';
import 'package:path/path.dart';
void main() {
GlobalKey<NavigatorState> navigator = new GlobalKey<NavigatorState>();
HttpOverrides.global = new AppHttpOverrides();
var navContent;
excludeBottomNavigationBar(){
return Container(
height: 0.0,
);
}
Map<String, WidgetBuilder> _routes = <String, WidgetBuilder>{
"/alerts": (BuildContext context){
navContent = myBottomNavigationBar();
new Alerts();
},
"/login": (BuildContext context){
navContent = excludeBottomNavigationBar();
new LoginPage();
},
"/settings": (BuildContext context){
navContent = myBottomNavigationBar();
new Settings();
},
"/enroll": (BuildContext context){
navContent = myBottomNavigationBar();
new Enroll();
},
"/add_device": (BuildContext context){
navContent = myBottomNavigationBar();
new AddDevice();
},
"/history": (BuildContext context){
navContent = myBottomNavigationBar();
new History();
},
"/home": (BuildContext context){
navContent = myBottomNavigationBar();
new Home();
},
"/device_settings": (BuildContext context){
navContent = myBottomNavigationBar();
new DeviceSettings()
},
"/geofence_list": (BuildContext context){
navContent = myBottomNavigationBar();
new GeofenceList()
},
"/draw_geofence": (BuildContext context){
navContent = myBottomNavigationBar();
new DrawGeofence()
},
};
runApp(new MaterialApp(
navigatorKey: navigator,
home: new SplashScreen(),
builder: (context, child) {
return new Scaffold(
body: child,
bottomNavigationBar:navContent,
resizeToAvoidBottomPadding: false
);
},
theme: buildTheme(),
routes: _routes,
));
}
I solved it by adding an instance constructor to my main state widget.
class MyAppState extends State<MyApp> {
var _currentIndex = 0;
static MyAppState instance = new MyAppState._();
MyAppState._();
// (Excluded Build and page routes)
Widget buildBottomNavigationBar(context) =>
BottomNavigationBar(
items: [
_buildBottomNavigationBarItem("A", Icons.add),
_buildBottomNavigationBarItem("B", Icons.remove),
],
onTap: onTabTapped,
currentIndex: _currentIndex,
);
_buildBottomNavigationBarItem(name, icon) =>
BottomNavigationBarItem(icon: Icon(icon), title: Text(name));
//Routing for BottomNavigationBar
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
// Navigate ...
});
}
}
Then you can declare bottomNavigationBar in any screen you want by adding this line to the desired Scaffold.
bottomNavigationBar: MyAppState.instance.buildBottomNavigationBar(context)
Related
I have pushed to two screen and wish to go back to my main home page. I tried doing that by using popUntil however it is not giving me the req result and just showing a black screen. Do i need to set a new route to my main page , even though i don't want to create a new instance of it ?
My code:
class Completed extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Completed Screen',
home: CompleteScreen(),
routes: <String, WidgetBuilder>{
// "/my-app": (BuildContext context) => MyApp()
}
);
}
}
class CompleteScreen extends StatelessWidget{
#override
Widget build(BuildContext context){
Container Complete = Container(
child: Column(
.....
FlatButton(
onPressed: (){
Navigator.popUntil(
context,
ModalRoute.withName('/'),
);
// Navigator.popUntil(context, ModalRoute.withName(Navigator.defaultRouteName));
},
),
],
));
return Scaffold(
body: Complete
);
}
}
My main page
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: DefaultTabController(length: 2,child: MyHomePage(title: '')),
routes: <String, WidgetBuilder>{
"/TaskScreen": (BuildContext context) => new task(),
}
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
Widget build(BuildContext context){
final list = ListView.builder(
itemBuilder: (context, position) {
return Ink(
child: InkWell(
onTap: (){
Navigator.of(context).pushNamed("/TaskScreen");
},
child: Card(
...
),),); },);
return Scaffold(
...
}
}
I tried using '/TaskScreen' and '/my-app' however even that didn't work.
You could try this
Navigator.popUntil(
context,
ModalRoute.withName(
Navigator.defaultRouteName,
),
),
As defaultRouteName works as the first screen opened when the app starts.
EDIT
So, as mentioned below, named routes won't work with Navigator.defaultRouteNamenor route.isFirst, the best approach to solve this I've found is declaring all your routes in the main page, as these will become global (or that's what I understood), so your code would end something like this
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
home: DefaultTabController(length: 2,child: MyHomePage(title: '')),
routes: <String, WidgetBuilder>{
"/": (BuildContext context) => MyApp(), (or MyHomePage())
"/TaskScreen": (BuildContext context) => new task(),
}
);
}
}
With that done, anytime you want to go back to the main page you just have to call
Navigator.popUntil(context, ModalRoute.withName('/'));
Hope that works for you.
The route in the popUntil has a property called isFirst that returns true if the route is the first route in the navigator. So in your case, you can use something like:
Navigator.of(context).popUntil((route) {
return route.isFirst;
});
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!
Did anyone successfully implemented WebSocket using json_rpc_2 package?https://pub.dartlang.org/packages/json_rpc_2
I try to present live data, e.g. a ticker, from this API: https://api.hitbtc.com/
Actually, I managed to solve the problem. Here's the code:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
import 'package:web_socket_channel/io.dart';
class SymbolDetails extends StatelessWidget {
final String symbolId;
SymbolDetails({this.symbolId});
#override
Widget build(BuildContext context) {
var _api = IOWebSocketChannel.connect('wss://api.hitbtc.com/api/2/ws');
var client = json_rpc.Client(_api.cast());
client.sendNotification(
'subscribeTicker',
{'symbol': '$symbolId'},
);
return Scaffold(
appBar: AppBar(
title: Text('$symbolId details'),
),
body: StreamBuilder(
stream: _api.stream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.none) {
return Center(
child: Text('Please check your internet connection'),
);
} else if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
String _snapshotData = snapshot.data;
Map _response = json.decode(_snapshotData);
return ListView(
children: [
ListTile(
title: Text('Ask price:'),
trailing: Text(
'${_response['params']['ask']}',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
ListTile(
title: Text('Bid price:'),
trailing: Text(
'${_response['params']['bid']}',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
],
);
},
),
);
}
}
I came up with a more fleshed-out example that runs on MongoDB. It is also null-safe. Here is the API I came up with:
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_web_socket/shelf_web_socket.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:mongo_dart/mongo_dart.dart' as mongo;
void main() async {
final handler = webSocketHandler((WebSocketChannel socket){
final server = Server(socket.cast<String>());
server.registerMethod('hello', () async{
mongo.Db db = mongo.Db('mongodb://127.0.0.1:27017/obj1');
await db.open();
var list = await db.collection('Person').find(null).toList();
// await db.close();
return list;
});
server.listen();
});
final server = await shelf_io.serve(handler, '127.0.0.1', 4042);
print('Serving at ws://${server.address.host}:${server.port}');
}
Here is the Flutter client:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
import 'package:web_socket_channel/io.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
const MyHomePage({super.key, required this.title});
#override
Widget build(BuildContext context) {
var api = IOWebSocketChannel.connect(Uri.parse('ws://127.0.0.1:4042'));
var client = json_rpc.Client(api.cast());
client.sendRequest(
'hello',
);
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: StreamBuilder(
stream: api.stream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.none) {
return const Center(
child: Text('Please check your internet connection'),
);
} else if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator());
}
String snapshotData = snapshot.data;
Map<String,dynamic> raw = json.decode(snapshotData);List items = raw['result'];
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text('${items[index]["first_name"].toString()} ${items[index]["last_name"].toString()}'),
);
},
);
},
),
);
}
}
When all goes well, the end result is a formatted list of names.
I built an app to use Shared Preferences package to store a user's chosen locale that will override whatever is the current locale by following this example.
The example worked as it should but currently I am trying to find a way to combine the shared preferences package with the tutorial so that users could save their language of choice.
This is the current code:
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_localization_intl/locale/locales.dart';
import 'dart:async';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
#override
MyAppState createState() {
return new MyAppState();
}
}
Future<bool> saveLocalePreference(SpecifiedLocalizationDelegate delegate) async{
SharedPreferences preferences = await SharedPreferences.getInstance();
preferences.setString("delegate", delegate.toString());
return preferences.commit();
}
class MyAppState extends State<MyApp> {
bool isSaved = false;
SpecifiedLocalizationDelegate _localeOverrideDelegate;
#override
void initState() {
_localeOverrideDelegate = new SpecifiedLocalizationDelegate(null);
super.initState();
}
onLocaleChange(Locale locale) {
setState(() {
_localeOverrideDelegate = new SpecifiedLocalizationDelegate(locale);
saveLanguage(_localeOverrideDelegate);
});
}
void makeDialog(){
showDialog(
context: context,
builder: (_) => new AlertDialog(
content: new Text("Locale has been Saved!")
)
);
}
void saveLanguage(SpecifiedLocalizationDelegate delegate){
saveLocalePreference(delegate).then((bool commited){
isSaved = commited;
setState(() {
commited = true;
print(commited);
});
makeDialog();
});
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
localizationsDelegates: [
_localeOverrideDelegate,
AppLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: [
Locale('en', ""),
Locale('es', ""),
Locale('fr', "")
],
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(onLocaleChange: onLocaleChange),
);
}
}
typedef void LocaleChangeCallback(Locale locale);
class MyHomePage extends StatefulWidget {
final LocaleChangeCallback onLocaleChange;
MyHomePage({Key key, this.onLocaleChange}) : super(key: key);
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
Locale myLocale = Localizations.localeOf(context);
print(myLocale);
return new Scaffold(
appBar: new AppBar(
title: new Text(
AppLocalizations.of(context).title
),
),
body: new Center(
child: new Column(
children: <Widget>[
new MaterialButton(
child: new Text(AppLocalizations.of(context).buttonText),
onPressed: (){}),
new MaterialButton(
child: new Text("ENGLISH"),
onPressed: (){
widget.onLocaleChange(const Locale("en", ""));
}),
new MaterialButton(
child: new Text("SPANISH"),
onPressed: (){
widget.onLocaleChange(const Locale("es", ""));
}),
new MaterialButton(
child: new Text("FRENCH"),
onPressed: (){
widget.onLocaleChange(const Locale("fr", ""));
}),
new MaterialButton(
child: new Text("DEFAULT"),
onPressed: (){
widget.onLocaleChange(null);
}),
],
),
),
);
}
}
The problem is:
The shared preferences does not seem to work as it will still go back to the default language whenever the app restarts. I tried removing the initState() but it will return this error -
I/flutter ( 5254): The getter 'type' was called on null.
I/flutter ( 5254): Receiver: null
What can I do to make this work?
did you try ?
if(delegate!=null){
preferences.setString("delegate", delegate.toString());
}