alexeyinkin / flutter-app-state Goto Github PK
View Code? Open in Web Editor NEWA bloc-based state management solution on top of Router API for larger apps.
License: MIT No Attribution
A bloc-based state management solution on top of Router API for larger apps.
License: MIT No Attribution
This is done in a Navigator
with pages when returning true
from onPopPage()
.
In PageStackBlocNavigator
, we currently check if the page is a BlocMaterialPage
i.e. it has a bloc with a power to prevent the pop. So we call onBackPressed()
on it. Ideally we want to await for bloc.onBackPressed to see if it handled the event (so we ignore it) or not (so we propagate it). But onPopPage
is synchronous, so we always have to ignore it and never return true
. If awaiting showed that we need to pop the page, we do so in _onBackButtonPressedInBloc
. But by then we can no longer close the app by onPopPage
result.
The options are:
File an issue with Flutter and suggest opPopPage be changed to FutureOr, see if it is filed already. Check if it is filed already.
When we determine that we need to pop the last page, close the app manually. This is complicated though, because the page could be the last one before asynchronous gap, but may become the last one afterwards.
Currently PageBloc
is a plain class. A request was to allow for blocs from https://pub.dev/packages/bloc
while others may want to use ChangeNotifier
.
And currently PageBloc
is not a bloc anyway because it has no state. We even have PageStatefulBloc
.
Currently we have didPopNext
which is called when the page above pops. It gives no guarantee that we are now topmost because there could still be pages above the one that has just popped.
A few points to consider:
Page
is disposed when popped so it cannot be pushed again.AbstractPage
is pushed, but PageStateMixin
is popped. So even if we could pop-and-push, that should be done on different objects.PageStateMixin
is also disposed on pop.The best workaround so far is to:
clone()
method on PageStateMixin
implementation.Page
implementation that will accept a given state object instead of creating a new one.Possible API improvements:
bool dispose = true
named parameter to PageStateMixin.pop()
to avoid cloning.AbstractPage
to PageStateMixin
so a state can grab its state and do something about it (clone it using some base method without requiring each page to have a clone
method?). I do not like it because it makes a state more 'widgety'.PageStack.pushState()
that will create a page for the state using the page factory. I do not like it because it adds indirection (the factory).Page
in another Navigator
once it had been used in some Navigator
. If so, reconsider why Page
and state are separate objects. I do not remember all limitations that led to this design. Can experiment with merging them.I am unable to run the 4_tabs example. I changed the folder name to tabs4 after android studio complained that 4_tabs is not a valid package name. Then I did
flutter create .
in the tabs4 folder but I do not get a runnable project.
When applying a state, all pages can be potentially popped, and new ones may fail to be created. Still we require at least one page in the bloc. We should probably throw an exception and roll back the whole call.
Currently PageStackBlocNavigator
uses StreamBuilder
to rebuild on each event from PageBloc
. This results in false positives.
There is currently _schedulePageDisposal
method.
If we dispose the page immediately (or even with 1 frame delay of Future.delayed(Duration.zero))
, the bloc will dispose TextEditingController
objects, and we get the exception of using disposed controllers before the screen is gone.
The current horrible fix is to schedule the disposal 10 seconds into the future in hope this is enough for the screen to go. This is not guaranteed though.
go_router allows to replace the entire stack of pages with another stack as if the URL was typed in.
To do the same, currently we need to pop all the pages from PageStackBloc
and add new pages one by one from the factory.
We need:
pageStackBloc.replacePages(pages)
to pop old pages and add new ones. This may also change the bottom-most page which is currently unchangeable.pageStackBloc.replaceWithPath(pagePath)
to create the default stack for the path. It will call the former.Say you got dynamic tabs which user can define them dynamically. Using pageStacks you can now add a new Stack or replace existing one but you can't seem remove or recreate whole stack.
Instead of the default MaterialPageRoute
I want to push ModalBottomSheetRoute
, DialogRoute
and CupertinoPageRoute
and handle the custom routeTransitions of them
PageStack
currently needs some bottomPage
to show.
If the app does not have a definite default page, users must create their own blank page to show before any page is pushed.
Example: https://github.com/apache/beam/blob/master/playground/frontend/lib/pages/loading/page.dart
Need a clean solution for this. I brainstormed the following options:
app_state
, require users to manually add it as bottomPage
.bottomPage
nullable and automatically add it if null
.bottomPage
parameter and just implicitly add this page at the bottom of any PageStack
.PageStack
to be emptied and implicitly show such page for empty PageStack
. Allow to override it with any widget.The use case is a splash screen starts and shows for 3 seconds and then navigates / transitions to a welcome screen. When running it gets 'stuck' on the splash screen and we never see the transition to the welcome screen. A sample project showing the issue is:
The usage of .replacePath() is in ui/splash/splash_screen.dart and looks like this:
class _SplashScreenState extends State<SplashScreen> {
final AppState appState = AppState();
bool _initialized = false;
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (!_initialized) {
_initialized = true;
Timer(const Duration(milliseconds: 3000), () {
appState.splashFinished();
//pageStack.go(const WelcomePagePath()); //<-- uncomment to see it 'work'
pageStack.replacePath(const WelcomePagePath()); //<-- doesn't work
});
}
}
I was able to make it work as expected by creating an extensions as follows:
import 'package:app_state/app_state.dart';
extension GoPageStack on PageStack {
go(PagePath pagePath) {
replacePath(pagePath);
popUntilBottom();
}
}
These are the easiest to start with.
Currently PageStackRouterDelegate
listens to all events from its PageStackBloc
and calls notifyListeners()
on each one. Yet the purpose of the delegate's notifications is to kick the Router
widget to rebuild. This currently results in false positive rebuilds.
The is currently MaterialPageStacksRouterDelegate
which is a PageStacksRouterDelegate
with Navigator
widget. It has a single MaterialPage
. Material is used here to pass child
as is, because ordinary Page
does not allow this. Still nothing material is used there. It may confuse cupertino users, and is just redundant.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.