What's the difference between Navigator.of(context).pop and Navigator.pop(context)?
To me both seems to do the same work, what is the actual difference. Is one deprecated?
Navigator.push(context, route) vs Navigator.of(context).push(route)
Navigator is used to manage the app's stack of pages(routes). When push the given route onto the screen(Navigator), We need to get the right Navigator and then push.
Navigator.of(context).push(route) splits .of(context) to get the right Navigator and .push(route). Navigator.of(context) has optional parameters, if rootNavigator is set to true, the NavigatorState from the furthest is given instead.
static NavigatorState of(
BuildContext context, {
bool rootNavigator = false,
bool nullOk = false,
})
Navigator.push(context, route) is a static method and do both at the same time. It internally calls Navigator.of(context).push(route). The navigator is most tightly encloses the given context.
static Future<T> push<T extends Object>(BuildContext context, Route<T> route) {
return Navigator.of(context).push(route);
}
pop() is similar to push().
When multiple Navigators are nested in App. The dialog route created by showDialog(...) method is pushed to the root navigator. If the application has multiple Navigator objects, it may be necessary to call Navigator.of(context, rootNavigator: true).pop(result) to close the dialog rather than just Navigator.pop(context, result).
There is no difference between the two, source code confirms this. Calling
Navigator.pop(context)
actually calls
Navigator.of(context).pop()
Source code:
static bool pop(BuildContext context, [ dynamic result ]) {
return Navigator.of(context).pop(result);
}
A little bit (not really a little) late to this but the main difference I notice between these two is that Navigator.pop(context) calls Navigator.of(context).pop() with the current widget's BuildContext.
Basically Navigator.of(context).pop() gets a NavigatorState from the passed context and pops the top-most route off the navigator. So when you use it directly, it pops the top-most route off the parent's Navigator (which is the route you are currently on). When you do Navigator.pop(context), you are doing Navigator.of(<current widget's context>).pop() which usually does the same thing since the current widget is usually on the top-most route.
To see differences between objects, you can try to check their hash code. For example with the code below, you can see if the function is being called with the same instance of BuildContext.
final navigatorState = Navigator.of(context);
print(navigatorState.context.hashCode); // Prints the parent's context's hash code.
print(context.hashCode); // Prints the current widget's context's hash code.
BUT this can become tricky when for example you call showDialog on the current context and your widget rebuilds, while the Dialog is still showing. In this case, If you try to pop the dialog with a Navigator.pop(context) you may encounter an Exception such as:
The following assertion was thrown while handling a gesture:
Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer stable.
To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by
calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
In this use case, using Navigator.of(context).pop() would be better.
Related
My use case is in constructing a WidgetApp in flutter:
new WidgetsApp(
pageRouteBuilder: <Contructor for MaterialPageRoute here>,
...
);
Instead of referencing the constructor, I'm just wrapping it in a function:
PageRoute pageRouteBuilder(RouteSettings settings, WidgetBuilder builder) {
return MaterialPageRoute(settings: settings, builder: builder);
}
And then referencing that function later:
new WidgetsApp(
pageRouteBuilder: pageRoutebuilder,
...
);
It used to be possible at some point in the history of Dart (like spread operator).
It is currently not possible though, but the feature may come back at some point.
In the meantime, you can use refactoring options for them to generate some of the boilerplate.
Constructor tear-off are not supported in Dart (https://github.com/dart-lang/sdk/issues/10659)
But even if it was supported, your code cannot really use it. MaterialPageRoute takes settings and builder as named parameters but PageRouteFactory takes 2 positionals parameters. So it wouldn't match.
You can write it like:
new WidgetsApp(
pageRouteBuilder: (settings, builder) => MaterialPageRoute(settings: settings, builder: builder),
);
The automatic inference, allows you to omit the type in the parameter of the closure.
I'm using a BLoC to keep state between two nested FullScreenDialogs.
I'm initializing the bloc when I push the first screen, like so
return FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) => ProductBlocProvider(child: ProductEntryScreen()),
fullscreenDialog: true
));
},
);
ProductEntryScreen has a bunch of TextFields and a button than opens a new FullScreenDialog. This new Screen also has TextFields.
The problem I'm having is that every time I write on a TextField on the second FullScreenDialog, the onPressed function where I start the ProductBlocProvider runs again.
And that re-run is causing the Bloc to create a new instance, so I end up loosing the state.
What I want to do?
Maybe I'm doing it wrong so I'll explain what I'm trying to achieve.
I want to keep state between the two FullScreenDialogs while I fill all the fields, and when I'm done I want to press a button that send all of the data (both screens) to a database.
The problem is that I was creating the instance of the bloc inside the provider in the builder function of the MaterialPageRoute.
That builder function was being called repeatedly, and creating a new instance of the bloc every time. The solution was to take out from the builde function the creation of the bloc instance, like this:
return FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
//Here I create the instance
var _bloc = ProductBloc();
Navigator.of(context).push(MaterialPageRoute(
//And I pass the bloc instance to the provider
builder: (BuildContext context) => ProductBlocProvider(bloc: _bloc, child: ProductEntryScreen()),
fullscreenDialog: true
));
},
);
The package get_it may be of help to you. get_it is a service locator library, and uses a Map to store the registered objects; therefore, it provides access at a complexity of O(1), which means it's incredibly fast. The package comes with a singleton GetIt which you can use like so,
// Create a global variable (traditionally called sl or locator)
final sl = GetIt.instance; // There is also a shorthand GetIt.i
// ...
// Then, maybe in a global function called initDi(),
// you could register your dependencies.
sl.registerLazySingleton(() => ProductBloc());
registerLazySingleton() or registerSingleton() will always
return the same instance; lazily (i.e., when first called)
or at app start-up respectively.
If you want to create a new instance every time, use registerFactory() instead (I put this here even though it's not exactly what you want).
For example,
sl.registerFactory(() => ValidatorCubit());
And it could be accessed like this,
MultiBlocProvider(
providers: [
// The type is inferred here
BlocProvider<AuthenticationBloc>(create: (_) => sl()),
// The type is explicitly given here
BlocProvider(create: (_) => sl<ProductsBloc>()),
],
child: ProductsScreen(),
),
This example primarily shows you how it can be done with the flutter_bloc library, but get_it works anywhere, even in non-flutter dart projects.
If you need more functionality, do make sure to read the docs for this package. It is well documented, and contains (almost) every feature you might need, including scoping.
Also, this approach allows you to use the interface pattern, making the code much more maintainable and testable, as you will have to change just one place to use a different implementation.
I have a Drawer that i share in all my StatefulWidget like this
#override
Widget build(BuildContext context) {
return new Scaffold(
key: _scaffoldKey,
drawer: SharedDrawer()
... //More code
In the Drawer i put a LogOut button that redirect the user to the login page cleaning all the router stack like this.
Navigator.of(context).pop();
Navigator.of(context).pushNamedAndRemoveUntil('/', (Route<dynamic> route) => false);
but when i try to log in again to the app this error appears.
flutter: Looking up a deactivated widget's ancestor is unsafe. At this
point the state of the widget's element tree is no longer stable. To
safely refer to a widget's ancestor in its dispose() method, save a
reference to the ancestor by calling inheritFromWidgetOfExactType() in
the widget's didChangeDependencies() method.
what i'm doing wrong ?
how i can manage the login/out in the app or is something with the navigator stack ?
Regards!
The error "Looking up a deactivated widget's ancestor is unsafe" is usually caused by reusing reference of Widgets that has been disposed previously i.e. during Navigator.of(context).pop();
One way to solve this issue is to keep track the context that you're using on your widgets so it won't reuse previoulsy disposed contexts.
This is mostly a conceptual question as I'm new to Dart and I guess I'm not understanding the semantics of the language here.
In IndexedWidgetBuilder,
Widget IndexedWidgetBuilder (
BuildContext context,
int index
)
who exactly is giving a value to index?
When this "thing" is used, for example:
itemBuilder: (context, i) {
blablabla
},
"context" and "i" are never initialized and they magically have a value. Who is defining this value and where?
IndexedWidgetBuilder is a typedef that defines a function that takes a BuildContext and an int, and returns a Widget.
/// Signature for a function that creates a widget for a given index, e.g., in a
/// list.
///
/// Used by [ListView.builder] and other APIs that use lazily-generated widgets.
typedef Widget IndexedWidgetBuilder(BuildContext context, int index);
So, when defining itemBuilder you are providing the Widget with a function that it can call, when it wants to build an item. When the Widget is building itself it will call this function many times to build each of its children. Simplistically, it may call this function with i=0, then 1, then 2, etc.
If your Widget only has 3 children, it would be easier to just pass then as a List, but if your Widget has a thousand children this would be inefficient - and this is where the builder comes in. The Widget will try to only call the itemBuilder function for the child widgets that it actually needs, and not for any that are, say, off the top or bottom of the screen.
So, to answer your question, the Widget passes the context and i to your itemBuilder function when it calls it (typically multiple times) to build some or all of its children. i represents the ith child, so that your builder function knows which child it is being asked to build.
Edit
The dartdoc of IndexedWidgetBuilder says that it is used by ListView.builder.
The dartdoc of ListView.builder says
Providing a non-null itemCount improves the ability of the [ListView]
to estimate the maximum scroll extent. The itemBuilder callback will
be called only with indices greater than or equal to zero and less
than itemCount. The itemBuilder should actually create the widget
instances when called.
The ListView.builder named constructor constructs a SliverChildBuilderDelegate, passing in the itemBuilder as builder.
It gets used in the SliverChildBuilderDelegate's build method, here:
#override
Widget build(BuildContext context, int index) {
assert(builder != null);
if (index < 0 || (childCount != null && index >= childCount))
return null;
Widget child = builder(context, index); // <- your callback is called
if (child == null)
return null;
if (addRepaintBoundaries)
child = new RepaintBoundary.wrap(child, index);
if (addAutomaticKeepAlives)
child = new AutomaticKeepAlive(child: child);
return child;
}
So, index comes from the SliverChildBuilderDelegate's build method. You could keep walking backwards to see who calls that.
I have a simple UI class
public class HelloWorldUI extends UI {
#Override
protected void init(VaadinRequest request) {
System.out.println("Initialized !");
final VerticalLayout layout = new VerticalLayout();
layout.addComponent(new Label("Hello World !"));
setContent(layout);
}
#Override
public void detach() {
System.out.println("Detach !");
super.detach();
}
#Override
public void attach() {
System.out.println("Attach !");
super.attach();
}
}
When first time my UI was loaded , I see outputs at my console as
Attach !
Initialized !
It is OK and this is what I expected. But when I refresh the browser , my console outputs were
Attach !
Initialized !
Detach !
Amazing ! I think Detach ! may be produce first because (as I think) when browser was refreshed , detach() method should be call and attach() , init() should be follow . But actually detach() method will call after attach() method. What's wrong my thinking ?
Browser Refresh = New UI Instance
When you refresh a browser window or tab, a new UI instance is created. So you see an attach message of a new UI instance. The old UI instance will be detached later.
This is default behavior in Vaadin 7. You may change that behavior with an annotation.
#PreserveOnRefresh
Adding #PreserveOnRefresh annotation to the UI changes the behavior: No new UI instance won't be created on refresh.
To quote the doc for this annotation:
Marks a UI that should be retained when the user refreshed the browser window. By default, a new UI instance is created when refreshing, causing any UI state not captured in the URL or the URI fragment to get discarded. By adding this annotation to a UI class, the framework will instead reuse the current UI instance when a reload is detected.