Flutter : Navigator operation requested with a context that does not include a Navigator - flutter

I have a scenario wherein I check the value of SharePreferences based on the value it will redirect the user to HomePage or LandingPage. I am not sure where did I got wrong? but I am getting this error below: I guess its not getting the context right any idea how do I get it?.
Unhandled Exception: Navigator operation requested with a context that does not include a Navigator.
E/flutter (11533): The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.
Here is my code:
import 'package:credit/src/pages/landing.dart';
import 'package:flutter/material.dart';
import 'package:credit/src/pages/credit/home.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
_LoadingPageState createState() => _LoadingPageState();
}
class _LoadingPageState extends State<MyApp> {
#override
void initState() {
super.initState();
getUserStatus().then((userStatus) {
if (userStatus == null) {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
return LandingPage();
}));
} else {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
return HomePage();
}));
}
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: CircularProgressIndicator(),
));
}
}
Future<String> getUserStatus() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String userStatus = prefs.getString('userstatus');
print("==On Load Check ==");
print(userStatus);
return userStatus;
}

When you call Navigator.of(context) framework goes up in widget tree attached to provided context and tries to find the closest Navigator.
The widget tree you showed does not have one, so you need to include Navigator in the widget tree.
Easiest option is to use MaterialApp with your widget passed as home. MaterialApp is creating navigator inside itself. (CupertinoApp does it too)
Updated code from original example:
import 'package:credit/src/pages/landing.dart';
import 'package:flutter/material.dart';
import 'package:credit/src/pages/credit/home.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: LoadingPage(),
);
}
}
class LoadingPage extends StatefulWidget {
LoadingPage({Key key}) : super(key: key);
_LoadingPageState createState() => _LoadingPageState();
}
class _LoadingPageState extends State<LoadingPage> { // note type update
#override
void initState() {
super.initState();
getUserStatus().then((userStatus) {
if (userStatus == null) {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
return LandingPage();
}));
} else {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
return HomePage();
}));
}
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: CircularProgressIndicator(),
));
}
}
Future<String> getUserStatus() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String userStatus = prefs.getString('userstatus');
print("==On Load Check ==");
print(userStatus);
return userStatus;
}

I have changed my code from
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo App',
theme: ThemeData(
primarySwatch: white,
scaffoldBackgroundColor: Colors.white,
),
home: Scaffold(
appBar: AppBar(
title: Text('Demo App'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
HomeScreen(title: 'Demo Home')));
},
child: Text('Open Home Screen'))
],
),
),
),
);
}
To
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo App',
theme: ThemeData(
primarySwatch: white,
scaffoldBackgroundColor: Colors.white,
),
home: InitScreen());
}
}
class InitScreen extends StatelessWidget {
const InitScreen({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Demo App'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
HomeScreen(title: 'Demo Home')));
},
child: Text('Open Home Screen'))
],
),
),
);
}
What changed?
Create a separate widget for home code in MyApp with InitScreen
What was the issue?
When we try to push Route by using Navigator.of(context), flutter will
try to find Navigator in the widget tree of the given context. In the
initial code, there was no widget that has Navigator. So, create a
separate widget for home code. And the MaterialApp widget in MyApp
will have Navigator.

Related

Flutter splash screen error - Navigator operation requested with a context that does not include a Navigator. How can I solve this error

Edit: (main.dart)
Added Sentry which actually starts the app
Future<void> main() async {
await SentryFlutter.init(
(options) {
options.dsn = _sentryDSN;
// Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.
// We recommend adjusting this value in production.
options.tracesSampleRate = _sentryTracesSampleRate;
options.attachStacktrace = true;
options.enableAppLifecycleBreadcrumbs = true;
},
appRunner: () => runApp(const SplashScreen()),
);
// or define SENTRY_DSN via Dart environment variable (--dart-define)
}
New to flutter, creating a splash screen to an app that was built with MaterialApp but getting an error. HOw can I solve this without a onPress function
Error:
Exception has occurred.
FlutterError (Navigator operation requested with a context that does not include a Navigator.
The context used to push or pop routes from the Navigator must be that of a widget that is a descendant of a Navigator widget.)
import 'package:flutter/material.dart';
import 'package:loopcycle/screens/loopcycle_main.dart';
class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key);
#override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
_navigateToMainApp();
}
void _navigateToMainApp() async {
await Future.delayed(const Duration(milliseconds: 2000), () {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => const LoopcycleMainApp()));
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Builder(
builder: (context) => const Center(
child: Text("test"),
)),
);
}
}
Thank you in advance.
EDIT: I changed the solution after you provided more information about the code.
This error is happening because you are using a context that does not have a Navigator in it, this is happening probrably because the widget that you are getting the context is parent of the MaterialApp() widget, to solve it you should create another widget that is a child of the MaterialApp() instead of using the parent widget, let me give you an example instead:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: GestureDetector(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SomeWidget(),
),
),
child: Container(
height: 300,
width: 300,
color: Colors.red,
),
),
);
}
}
This may give an error because you are using the context of a widget that is the parent of the MaterialApp() widget, to solve it just create another widget that is a child of MaterialApp().
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: AnotherWidget(),
);
}
}
class AnotherWidget extends StatelessWidget {
const AnotherWidget({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SomeWidget(),
),
),
child: Container(
height: 300,
width: 300,
color: Colors.red,
),
),
);
}
}
I was playing with your code, and fixed it for you, and there are basically two ways to solve it, you can create a MaterialApp() before calling the SplashScreen() in the runApp() function like so:
import 'package:flutter/material.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:loopcycle/screens/loopcycle_main.dart';
Future<void> main() async {
await SentryFlutter.init(
(options) {
options.dsn = _sentryDSN;
// Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.
// We recommend adjusting this value in production.
options.tracesSampleRate = _sentryTracesSampleRate;
options.attachStacktrace = true;
options.enableAppLifecycleBreadcrumbs = true;
},
appRunner: () => runApp(
const MaterialApp(
home: SplashScreen(),
),
),
);
// or define SENTRY_DSN via Dart environment variable (--dart-define)
}
class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key);
#override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
_navigateToMainApp();
}
void _navigateToMainApp() async {
await Future.delayed(const Duration(milliseconds: 2000), () {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => const LoopcycleMainApp()));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Builder(
builder: (context) => const Center(
child: Text("test"),
)),
);
}
}
Or you can create an intermediate widget to hold the MaterialApp() and then inside this widget you can call SplashScreen(), like so:
import 'package:flutter/material.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:loopcycle/screens/loopcycle_main.dart';
Future<void> main() async {
await SentryFlutter.init(
(options) {
options.dsn = _sentryDSN;
// Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring.
// We recommend adjusting this value in production.
options.tracesSampleRate = _sentryTracesSampleRate;
options.attachStacktrace = true;
options.enableAppLifecycleBreadcrumbs = true;
},
appRunner: () => runApp(const MyApp()),
);
// or define SENTRY_DSN via Dart environment variable (--dart-define)
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: SplashScreen(),
);
}
}
class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key);
#override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
#override
void initState() {
super.initState();
_navigateToMainApp();
}
void _navigateToMainApp() async {
await Future.delayed(const Duration(milliseconds: 2000), () {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => const LoopcycleMainApp()));
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Builder(
builder: (context) => const Center(
child: Text("test"),
)),
);
}
}
In this second solution, the intermediate widget is the MyApp() widget, and in my opinion, I consider this solution as being the best one for your problem, because if you ever wanted to load a different screen based on the different states, for example:
If a user is signed in you load a home page, and if a user is not signed in you load a sign up page.
Doing this, or anything similar is much easier when you have this intermediate widget that holds the MaterialApp(), and you can even create some logic to integrate the Splash Screen too, but I don't know what you are trying to achieve, so pick the solution you find the best for your problem.

Why state change error occurs on flutter_riverpod during initialization

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final dataProvider = StateNotifierProvider<DataNotifier, List<int>>((ref) {
return DataNotifier();
});
class DataNotifier extends StateNotifier<List<int>> {
DataNotifier() : super([]);
Future<void> getData() async {
state = [];
await Future.delayed(const Duration(seconds: 2));
state = [1, 2];
}
}
void main() => runApp(ProviderScope(child: App()));
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Home(),
);
}
}
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.white,
child: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => SecondPage()),
);
},
child: const Text('Next page'),
),
),
),
);
}
}
class SecondPage extends ConsumerStatefulWidget {
const SecondPage({Key? key}) : super(key: key);
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends ConsumerState<SecondPage> {
#override
void initState() {
super.initState();
ref.read(dataProvider.notifier).getData();
}
#override
Widget build(BuildContext context) {
final numbers = ref.watch(dataProvider);
return Scaffold(
appBar: AppBar(),
body: ListView.builder(
itemBuilder: (_, index) {
return Text('data: $index');
},
itemCount: numbers.length,
),
);
}
}
I am new to riverpod and I noticed this error while changing state.
In the above code when I tap the "next page" button at the fresh start for the first time it works as expected but when I go back and again tap the "next page" button, an error shown below is thrown:
StateNotifierListenerError (At least listener of the StateNotifier Instance of 'DataNotifier' threw an exception
when the notifier tried to update its state.
Does anyone know why this occurs and how can I prevent it.
You can solve the issue using autoDispose
final dataProvider = StateNotifierProvider.autoDispose<DataNotifier, List<int>>(
(ref) => DataNotifier(),
);
For Future I prefer using FutureProvider.
More about riverpod

Flutter Provider nested navigation

I have a problem with provider and navigation.
I have a HomeScreen with a list of objects. When you click on one object I navigate to a DetailScreen with tab navigation. This DetailScreen is wrapped with a ChangenotifierProvider which provides a ViewModel
Now, when I navigate to another screen with Navigator.of(context).push(EditScreen) I can't access the ViewModel within the EditScreen
The following error is thrown
════════ Exception caught by gesture ═══════════════════════════════════════════
The following ProviderNotFoundException was thrown while handling a gesture:
Error: Could not find the correct Provider<ViewModel> above this EditScreen Widget
This is a simple overview of what I try to achieve
Home Screen
- Detail Screen (wrapped with ChangeNotifierProvider)
- Edit Screen
- access provider from here
I know what the problem is. I'm pushing a new screen on the stack and the change notifier is not available anymore.
I thought about creating a Detail Repository on top of my App which holds all of the ViewModels for the DetailView.
I know I could wrap the ChangeNotifier around my MaterialApp, but I don't want that, or can't do it because I don't know which Detail-ViewModel I need. I want a ViewModel for every item in the list
I really don't know what's the best way to solve this. Thanks everyone for the help
Here is a quick example app:
This is a picture of the image tree
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
child: Text("DetailView"),
onPressed: () => Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ChangeNotifierProvider(
create: (_) => ViewModel(), child: DetailScreen()))),
)));
}
}
class DetailScreen extends StatelessWidget {
const DetailScreen({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
child: Text("EditScreen"),
onPressed: () => Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => EditScreen())),
),
));
}
}
class EditScreen extends StatelessWidget {
const EditScreen({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
child: Text("Print"),
onPressed: () =>
Provider.of<ViewModel>(context, listen: false).printNumber()),
),
);
}
}
class ViewModel extends ChangeNotifier {
printNumber() {
print(2);
}
}
To be able to access providers accross navigations, you need to provide it before MaterialApp as follows
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => ViewModel(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
child: Text("DetailView"),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => DetailScreen(),
),
),
)));
}
}
class DetailScreen extends StatelessWidget {
const DetailScreen({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
child: Text("EditScreen"),
onPressed: () => Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => EditScreen())),
),
));
}
}
class EditScreen extends StatelessWidget {
const EditScreen({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
child: Text("Print"),
onPressed: () =>
Provider.of<ViewModel>(context, listen: false).printNumber()),
),
);
}
}
class ViewModel extends ChangeNotifier {
printNumber() {
print(2);
}
}
A bit late to the party, but I think this is the answer the question was looking for:
(Basically passing the ViewModel down to the next Navigator page.)
class DetailScreen extends StatelessWidget {
const DetailScreen({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
final viewModel = Provider.of<ViewModel>(context); // Get current ViewModel
return Scaffold(
body: Center(
child: RaisedButton(
child: Text("EditScreen"),
onPressed: () => Navigator.of(context).push(
// Pass ViewModel down to EditScreen
MaterialPageRoute(builder: (context) {
return ChangeNotifierProvider.value(value: viewModel, child: EditScreen());
}),
),
),
));
}
}
I am a bit late but I found a solution on how to keep the value of a Provider alive after a Navigator.push() without having to put the Provider above the MaterialApp.
To do so, I have used the library custom_navigator. It allows you to create a Navigator wherever you want in the tree.
You will have to create 2 different GlobalKey<NavigatorState> that you will give to the MaterialApp and CustomNavigator widgets. These keys will allow you to control what Navigator you want to use.
Here is a small snippet to illustrate how to do
class App extends StatelessWidget {
GlobalKey<NavigatorState> _mainNavigatorKey = GlobalKey<NavigatorState>(); // You need to create this key for the MaterialApp too
#override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: _mainNavigatorKey; // Give the main key to the MaterialApp
home: Provider<bool>.value(
value: myProviderFunction(),
child: Home(),
),
);
}
}
class Home extends StatelessWidget {
GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>(); // You need to create this key to control what navigator you want to use
#override
Widget build(BuildContext context) {
final bool myBool = Provider.of<bool>(context);
return CustomNavigator (
// CustomNavigator is from the library 'custom_navigator'
navigatorKey: _navigatorKey, // Give the second key to your CustomNavigator
pageRoute: PageRoutes.materialPageRoute,
home: Scaffold(
body: FlatButton(
child: Text('Push'),
onPressed: () {
_navigatorKey.currentState.push( // <- Where the magic happens
MaterialPageRoute(
builder: (context) => SecondHome(),
),
},
),
),
),
);
}
}
class SecondHome extends StatelessWidget {
#override
Widget build(BuildContext context) {
final bool myBool = Provider.of<bool>(context);
return Scaffold(
body: FlatButton(
child: Text('Pop'),
onPressed: () {
Novigator.pop(context);
},
),
);
}
}
Here you can read the value myBool from the Provider in the Home widget but also ine the SecondHome widget even after a Navigator.push().
However, the Android back button will trigger a Navigator.pop() from the Navigator of the MaterialApp. If you want to use the CustomNavigator's one, you can do this:
// In the Home Widget insert this
...
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
if (_navigatorKey.currentState.canPop()) {
_navigatorKey.currentState.pop(); // Use the custom navigator when available
return false; // Don't pop the main navigator
} else {
return true; // There is nothing to pop in the custom navigator anymore, use the main one
}
},
child: CustomNavigator(...),
);
}
...

how to edit value at the child widget

i try to edit the value of the child widget, i can do it with StatefulWidget parent but i want to do it with StatelessWidget parent and without using global value
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(child: Text('addFile'), onPressed: () {}),
FlatButton(child: Text('deleteFile'), onPressed: () {})
],
),
body: Child(),
);
}
}
class Child extends StatefulWidget {
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
var hasFile = true;
#override
Widget build(BuildContext context) {
return hasFile ? Text('has a file') : Text("no File");
}
}
You are thinking the wrong way. Child aka Text() should get its value from a model which is managed by the application or at least managed by the widget above. I would go with the provider package https://pub.dev/packages/provider and do this:
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
class MyState with ChangeNotifier {
String _myText;
MyState(this._myText);
getMyText() => _myText;
void changeText(String newText) {
_myText = newText;
notifyListeners();
}
}
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(builder: (_) => MyState("initial Text")),
],
child: Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(
child: Text('addFile'),
onPressed: () {
Provider.of<MyState>(context).changeText("addFile");
}),
FlatButton(
child: Text('deleteFile'),
onPressed: () {
Provider.of<MyState>(context).changeText("deleteFile");
})
],
),
body: Child(),
));
}
}
class Child extends StatelessWidget {
#override
Widget build(BuildContext context) {
MyState myState = Provider.of<MyState>(context);
return Text(myState.getMyText());
}
}
This is coded without IDE support or even compiling and running. But it should get you to the right direction.
You can use BLoC pattern to implement this kind of functionality,
Here is the BLoC class which will handle state of bool
import 'dart:async';
class Bloc {
final _fileController = StreamController<bool>();
changeState(bool val) {
_fileController.sink.add(val);
}
get hasFile => _fileController.stream;
dispose() {
_fileController.close();
}
}
final bloc = Bloc();
Then you can add stream builder in your Stateful Widget, in which you will provide stream of BLoC class.
StreamBuilder updates it's UI according to Stream.
class Child extends StatefulWidget {
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
var hasFile = true;
#override
void dispose() {
bloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: bloc.hasFile,
initialData: false,
builder: (context, snapshot) {
return snapshot.data ? Text('has a file') : Text("no File");
},
);
}
}
At last you can access BLoC class with your stateless widget as follows
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(
child: Text('addFile'),
onPressed: () {
bloc.changeState(true);
}),
FlatButton(
child: Text('deleteFile'),
onPressed: () {
bloc.changeState(false);
})
],
),
body: Child(),
);
}
}
Full example is as below
import 'package:flutter/material.dart';
import 'dart:async';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Homepage(),
);
}
}
class Homepage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
FlatButton(
child: Text('addFile'),
onPressed: () {
bloc.changeState(true);
}),
FlatButton(
child: Text('deleteFile'),
onPressed: () {
bloc.changeState(false);
})
],
),
body: Child(),
);
}
}
class Child extends StatefulWidget {
#override
_ChildState createState() => _ChildState();
}
class _ChildState extends State<Child> {
var hasFile = true;
#override
void dispose() {
bloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: bloc.hasFile,
initialData: false,
builder: (context, snapshot) {
return snapshot.data ? Text('has a file') : Text("no File");
},
);
}
}
class Bloc {
final _fileController = StreamController<bool>();
changeState(bool val) {
_fileController.sink.add(val);
}
get hasFile => _fileController.stream;
dispose() {
_fileController.close();
}
}
final bloc = Bloc();

Flutter navigator.push() object issue

While passing an object from one class to another class by using Navigator.push(), the object does not get modifying even its declared as not final.
Main Screen : Created an object(userBean) and passing to First screen
First Screen : displaying the same object(userBean) values, and passing again the same object(userBean) to second screen.
Second screen : trying to get modify the same object (userBean) in second screen, and printing the same object(userBean) in first screen by using refreshData.then method.
Main.dart
import 'package:flutter/material.dart';
import 'package:flutter_app_poc1/firstSceeen.dart';
import 'package:flutter_app_poc1/secondScreen.dart';
import 'package:flutter_app_poc1/userbean.dart';
void main() => runApp(MyApp());
typedef void refreshCallBack(int index);
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
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> {
UserBean user = new UserBean();
final List<String> hhList = ["General", "edu"];
int _counter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new FlatButton(
child: Text("Next Screen"),
onPressed: () {
user.id = 1;
user.name = "Ramesh";
Future<dynamic> refreshData =
Navigator.of(context).push(new MaterialPageRoute<dynamic>(
builder: (BuildContext context) {
return new FirstScreen(userbean: user);
},
));
refreshData.then((_) {
});
}),
],
),
),
);
}
}
Firstscreen.dart
import 'package:flutter/material.dart';
import 'package:flutter_app_poc1/secondScreen.dart';
import 'package:flutter_app_poc1/userbean.dart';
typedef void refreshCallBack(int index);
class FirstScreen extends StatefulWidget {
UserBean userbean;
FirstScreen({Key key, this.userbean}) : super(key: key);
#override
_FirstScreenState createState() => _FirstScreenState();
}
class _FirstScreenState extends State<FirstScreen> {
String userName;
final List<String> hhList = ["General", "edu"];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("first"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(widget.userbean.name),
new RaisedButton(onPressed: (){
Future<dynamic> refreshData =
Navigator.of(context).push(new MaterialPageRoute<dynamic>(
builder: (BuildContext context) {
return new SecondScreen(userbean: widget.userbean);
},
));
refreshData.then((_) {
print(widget.userbean.name);
});
}),
],
),
),
);
}
}
secondscreen.dart
import 'package:flutter/material.dart';
import 'package:flutter_app_poc1/userbean.dart';
class SecondScreen extends StatefulWidget {
UserBean userbean;
SecondScreen({Key key, this.userbean}) : super(key: key);
#override
_SecondScreenState createState() => _SecondScreenState();
}
class _SecondScreenState extends State<SecondScreen> {
UserBean bean = UserBean();
#override
Widget build(BuildContext context) {
bean.name = "suresh";
return Scaffold(
appBar: AppBar(
title: Text("Previous Screen"),
),
body: Center(
child: new FlatButton(
child: Text(bean.name),
onPressed: () {
print(bean.name);
widget.userbean = bean;
Navigator.pop(context, true);
}),
));
}
}
#Murali
If you want to follow the same procedure pass object, then follow the below procedure.
From Navigator.pop push again new Object
onPressed: () {
print("TEST second screen :"+bean.name);
/// here modifying with new object.
widget.userbean = bean;
Navigator.pop(context, widget.userbean);
}),
In second screen Get new Object from Feature Method as below
Future<UserBean> refreshData =
Navigator.of(context).push(new MaterialPageRoute<UserBean>(
builder: (BuildContext context) {
return new SecondScreen(userbean: widget.userbean);
},
));
refreshData.then((res) {
print("TEST First screen : ${res.name}");
});
Then Object will change with new values.