brianegan / flutter_redux Goto Github PK
View Code? Open in Web Editor NEWA library that connects Widgets to a Redux Store
License: MIT License
A library that connects Widgets to a Redux Store
License: MIT License
Hi
This works fine
new Text(viewModel.game.duration.toString())
but this does not.
new Text(viewModel.game.formattedDuration(viewModel.game.duration),
Am I doing this wrong? Should I be formatting my duration inside the reducer?
(I'm on a slightly older version so I'm updating now)
Given
final store = new Store<int>(appReducer, initialState: 0);
final appReducer = combineTypedReducers(
[new ReducerBinding<int, Increment>((state, action) => state + 1)]);
class Increment{}
@override
Widget build(BuildContext context) {
return new StoreProvider<int>(
store: store,
child: new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
));
}
I get the following error when trying to dispatch an Increment
action:
type '(int, Increment) => int' is not a subtype of type '(int, dynamic) => int'
This is when running flutter run --preview-dart-2
. When running flutter run
, I get the follwing error:
The following StoreProviderError was thrown building StoreConnector<int, Store<int>>(dirty):
Error: No dynamic found. To fix, please try:
* Using Dart 2 (required) by using the --preview-dart-2 flag
* Wrapping your MaterialApp with the StoreProvider<State>, rather than an individual Route
* Providing full type information to your Store<State>, StoreProvider<State> and
StoreConnector<State, ViewModel>
flutter --version
Flutter 0.2.3 • channel beta • https://github.com/flutter/flutter.git
Framework • revision 5a58b36e36 (2 weeks ago) • 2018-03-13 13:20:13 -0700
Engine • revision e61bb9ac3a
Tools • Dart 2.0.0-dev.35.flutter-290c576264
Repro project can be found here: https://github.com/jonstodle/flutter_redux_reproduction
I am getting StoreProvider not found when I try connecting from another class. Hoping the code below helps understanding the issue.
return new StoreProvider<AppState>(
store: store,
child: new MaterialApp(
title: title,
theme: new ThemeData(
primaryColor: Colors.lightBlue,
),
home: new HomePage(), /// <<--- USING STORE_CONNECTOR AS BELOW FAILS WITHIN HOMEPAGE
// home: new StoreConnector<AppState, int>(
// converter: (storexx) => storexx.state.luckyNumber,
// builder: (context, count) {
// return new Text(count.toString());
// },
// ),
),
);
Hey, this is my first day of Dart and I found there was no way to persist to a redux store without writing the code yourself all the time. I made a small library called redux_persist to help with that.
I'd like to add a link to the library in the README, as this is probably a common pattern with flutter_redux!
Also, if you have a few minutes to space, check out the library and let me know of any issues you see on it! Thanks for flutter_redux!
Hi,
I'm trying to implement redux on my flutter app, versions:
redux: "^3.0.0"
flutter_redux: "^0.5.0"
When I run my app the error in the title is thrown.
I followed the Dart 2 migration guide for both the packages.
Flutter: 0.2.8
Dart: 2.0.0
I've been toying with the idea of introducing an additional callback, something like.
typedef OnWillChangeCallback<ViewModel> = bool Function(ViewModel viewModel);
The reason i found i wanted this was because there are times where it's desirable to trigger some form of navigation action as result of state change. You can't do that during the build action (as far as i understand), but you can do it in the IgnoreChangeCallback pre-build. This works, but feels odd for two reasons. One it happens before the distinct filter which sometimes is useful, and two it's passed the State and not the ViewModel so i can end up duplicating some conversion i've done for the viewModel.
Interested in hearing thoughts and opinions on approaches to dealing with this. I've made the above suggested change locally and using it, so i can submit a pull request if deemed a good idea.
Hi there.
How was your trip? 😄
I've been happily using flutter_redux
, but I'm having trouble with one thing in particular.
I want some methods inside my Widgets to dispatch actions to the Store from the StoreProvider
, but not necessarily build a widget.
Since StoreConnector()
is only intended for passing the store to a Widget in the context of the build method, I'm wondering: is there any class or function I can use inside of a method that returns void
(for example) that just provides me with the Store, so I can dispatch an action to it without needing to build a widget?
Thank you.
I'm having issues accessing the store from outside the flutter widget tree, and I'm not quite sure if the problem lies with me or if it's simply not possible.
Say I define a store in my main.dart
:
Store<AppState> store = new Store(counterReducer, initialState: new myAppState());
class FlutterReduxApp extends StatelessWidget {
// Create your store as a final variable in a base Widget. This works better
// with Hot Reload than creating it directly in the `build` function.
@override
Widget build(BuildContext context) {
return new MaterialApp(
theme: new ThemeData.dark(),
home: new StoreProvider(
store: store,
....
Then modify the state in the StoreProvider
's descendants and try to access it:
@override build(BuildContext context) {
String attrA = new StoreProvider.of(context).store.state.myAttribute;
String attrB = store.state.myAttribute; // Direct access `store` from `main.dart`
assert(attrA == attrB); // Fails
}
In the example above attrA
has the most recent state, but attrB
seems to have the store's initial state. Am I making a mistake somewhere or is this a limitation of flutter_redux
?
The reason I'd like to get the store singleton outside the widget tree is to add extra store-based functionality to my models, e.g. searching the store for related models etc...
Do you have any suggestions for how to to implement a StoreConnector for a PreferredSizeWidget?
Specifically the example I have is AppBar where bottom is a TabBar. The tabs may need to be updated if the app state changes.
Hi, I dunno if its possible to call showSnackBar from a middleware, but I want a Snackbar to show on the active page when I catch an error. Is there a better more redux-y way of doing this?
Might not be possible given the way the converter
is hooked up to the stream, but should be investigated further.
Note: From "arranf" on gitter.
Coming from a ReduxJS/React background it is very common to pass multiple items from a redux store to a child component (e.g. using both mapStateToProps
and mapDispatchToProps
).
I have not seen in any example where you may want to pass both a state and a dispatcher to the same child component.
Is it considered correct to returning a tuple from a StoreConnector
?
// Connect the Store to a FloatingActionButton. In this case, we'll
// use the Store to build a callback that with dispatch an Increment
// Action.
//
// Then, we'll pass this callback to the button's `onPressed` handler.
floatingActionButton:
new StoreConnector<int, Tuple2<int, VoidCallback>>(
converter: (store) {
// Return a Tuple2 - one like mapStateToProps and one mapDispatchToProps
// Return a `VoidCallback`, which is a fancy name for a function
// with no parameters. It only dispatches an Increment action.
return new Tuple2(
store.state, () => store.dispatch(Actions.Increment));
},
builder: (context, tuple) {
return new FloatingActionButton(
// Attach the `callback` to the `onPressed` attribute
onPressed: tuple.item2,
tooltip:
tuple.item1.isOdd ? 'Increment_ODD' : 'Increment_EVEN',
child: new Icon(Icons.add));
},
),
),
Hi, I don't know if I could ask a question in issues. If not, I am very sorry about this.
I am new to flutter and mobile development.
The Purpose
of this project in README.md
is very well written and I am glad the author wrote it. But I am still confused the difference between flutter_redux
and flutter_greencat
. They are created by the same author and the README.md mentions each other. I googled greencat and find out it is a port of redux from reactjs to Dart. In another word, it means greencat in dart is redux. But now, they become two different projects flutter_redux and flutter_greencat. I am wondering which one I should choose and will both projects well supported in the near future?
PS: can I use flutter_redux in production? since it's a very new project, I am wondering if it's stable to be used in produciton.
Thank you very much for your help.
Hey @brianegan
after updating to 0.4.0
Im getting the following error. Any idea what could cause the issue?
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞══
The following NoSuchMethodError was thrown building StoreConnector<AppState,
Store<AppState>>(dirty):
The getter 'store' was called on null.
Receiver: null
Tried calling: store
[✓] Flutter (Channel master, v0.2.4-pre.64, on Mac OS X 10.13.3 17D102, locale en-DE)
• Flutter version 0.2.4-pre.64 at /Users/eikebartels/Applications/flutter
• Framework revision 619ebd67a9 (5 hours ago), 2018-03-20 00:26:10 -0700
• Engine revision c3ab0c9143
• Dart version 2.0.0-dev.39.0.flutter-f1ebe2bd5c
void main() {
final store = new Store<AppState>(
appStateReducer,
initialState: new AppState.loading(),
middleware: createStoreMiddleware()
);
runApp(new App(store: store));
}
class App extends StatelessWidget {
final Store store;
App({Key key, this.store }) : super(key: key);
@override
Widget build(BuildContext context) {
return new StoreProvider<AppState>(
store: store,
child: new MaterialApp(
theme: AppTheme.theme,
routes: {
"/": (context) {
return new StoreBuilder<AppState>(
builder: (context, store) {
return new OnBoarding();
});
}
},
),
);
}
}
Hi. I'm just trying out redux for the first time. Sorry if this isn't the right place for design questions. I considered using Stackoverflow, but I've had these kinds of questions closed there in the past.
Basically, what I'm pondering is how much functionality will reside in middleware and whether it's really the right place for it. To make this discussion concrete, I've got two examples:
Consider the scenario where the user enters some text and that text needs to be validated+converted to an integer.
To implement this, I'm imagining the following:
rawInput
(a String
), isRawInputValid
(a bool
), convertedValue
(an int
) and possibly even a validationError
(a String
). All this could be wrapped up in a Validated<T>
class, which I could use for any validated field. Thus, the store itself would just have a single instance of Validated<int>
.SendRawInput
action would be defined, along with SendValidatedInput
.SendRawInput
action.SendRawInput
and perform the validation, then instead send through a SendValidatedInput
action.Now imagine the user chooses a DateTime
, which gets sent through to the store. Depending on the date, the view wants to display either "Yesterday", "Today", "Tomorrow" or some formatted date like "ddd MMM, yyyy".
Where does this formatting logic reside?
Similarly to the above, one could make the case for "formatting" middleware that intercepts a RawDateAction
, formats the date within, then forwards a Formatted<Date>
instance (which just contains the original value and a formatted value) onto the store via a separate action.
But is this the way it's generally done with redux? It seems like a lot of moving parts for something so simple. Moreover, I can imagine a store where there are a half dozen or more such fields, making things a little unwieldy.
One alternative to this I considered was some kind of view model - something that wraps a store instance and provides formatting over the top of it. However, that is also another moving part, and it's not clear to me whether it's really a good idea.
Any thoughts on this?
I just tried this, expecting it to work:
void _handleApprove(Store<AppState> store, Approve action, NextDispatcher next) {
next(action);
store.dispatch(new Save());
}
My expectation was that the Approve
reducer would execute, and then the Save
action would be instigated. In reality, the opposite is true.
Have I fundamentally misunderstood the middleware/reducer relationship here? Shouldn't forward an action onto the next piece of middleware eventuate in the action reaching the reducers before middleware continues execution?
Hello,
Is support for Dart 2 inbound? Are there tasks/issues raised to deal with supporting Dart 2? If so, i'm willing to help out.
I just migrated to the flutter Dart 2 preview and faced an enormous amount of different errors related to redux: https://github.com/flutter/flutter/wiki/Trying-the-preview-of-Dart-2-in-Flutter
Hi, I'm new to flutter/dart and I'm developing a simple use-case: 1 form across 2 pages
I want to persist data and be able to get the first screen data when I'm on the second view. After hours of research, I found this library and I can't get it to work properly for my case.
Can you provide a simple example with forms and dart 2.0 ? I think my issue is maybe related to #20, but mostly because I'm a beginner in the language.
It's all so foggy to me with controllers, focusNode, event propagation... I'm a backend guy, I'm used to simpler things.
My code is on this branch, I'm sorry for the mess, its a poc for now...
I've tried to understand your example on gitlab, but I don't understand every part of it
Hello! Here's another tasty design question with respect to redux and flutter...
I need to support undo in my app, so that users can wantonly and confidently enact destructive changes, and also so that state management is generally easier. In my case, I have a hierarchy of items, and items within those items. Users can add/edit/delete (and undo those deletions). So something as simple as hitting an ADD button, then hitting the navigation's back button needs to intelligently manage state.
My implementation is based on a stack of immutable (built) DTOs that are within my state. Then there are three actions to manage this state (BeginTransaction
, CommitTransaction
, RollbackTransaction
). This part was really easy to get up and running. The reducers just do what you'd expect, and include assertions to ensure that the state maintains its integrity (always at least one item on the stack).
The hard part was figuring out where to dispatch these actions and finding a way to somewhat guarantee that transactions will remain balanced. Initially, I just sprinkled them through my views. It worked, but was terrible and I'm pretty sure there were some edge cases that I'd missed. I abandoned that and attempted to manifest transactions solely within middleware. Sounds an attractive proposition, but it was difficult/impossible to achieve cleanly.
Here's a look at my middleware that handles this:
class Transactions extends NavigatorObserver {
Transactions(globals.CompositeNavigatorObserver compositeNavigatorObserver) {
compositeNavigatorObserver.addObserver(this);
}
List<Middleware<AppState>> getMiddlewareBindings() => [
new TypedMiddleware<AppState, AddEntry>(_beginTransactionReducer),
new TypedMiddleware<AppState, RemoveEntry>(_beginTransactionReducer),
new TypedMiddleware<AppState, AddAllowance>(_beginTransactionReducer),
new TypedMiddleware<AppState, RemoveAllowance>(_beginTransactionReducer),
];
void _beginTransactionReducer(Store<AppState> store, dynamic action, NextDispatcher next) {
_beginTransaction(store);
next(action);
}
void _beginTransaction(Store<AppState> store) {
store.dispatch(new BeginTimesheetTransaction());
}
void _commitTransaction(Store<AppState> store) {
store.dispatch(new CommitTimesheetTransaction());
}
void _rollbackTransaction(Store<AppState> store) {
store.dispatch(new RollbackTimesheetTransaction());
}
@override
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
var store = globals.store;
var name = route.settings.name;
if (name == RouteNames.editEntryJobDetails || name == RouteNames.editEntryAllowance) {
_beginTransaction(store);
}
}
@override
void didPop(Route<dynamic> route, Route<dynamic> previousRoute) {
var store = globals.store;
var name = route.settings.name;
if (route is MaterialPageRoute<dynamic>) {
if (name == RouteNames.addEntryJobDetails || name == RouteNames.editEntryJobDetails || name == RouteNames.addEntryAllowance || name == RouteNames.editEntryAllowance) {
route.completed.then((result) {
if (result == true) {
_commitTransaction(store);
} else {
_rollbackTransaction(store);
}
});
}
}
}
}
And here are the main problems I have with this approach:
CompositeNavigatorObserver
class, which just manages any number of NavigatorObserver
instances, forwarding all events onto them. I pass this CompositeNavigatorObserver
into my MaterialApp
. This allows the middleware to monitor and react to navigation events. I could not think of a cleaner way.Store
instance, and other times it's passed in so I don't have to. I also used Scaffold.of
at one point, but that was no better (and was messier).true
when editing completes successfully. This was also a bit messy (in a separate piece of middleware).OK, sorry for the rando post again. Really just interested in hearing any thoughts on this direction or how it could be improved.
Thanks!
Hi,
I need to have access to the store in the builder function so I can dispatch calls to the middleware. Is there a way to do this "nicely" without storing the store during the call to onInit?
I am new to dart and I just cannot see how to do this.
The use case I am looking at is having a widget display some stateful data and a button. The button when pressed should invoke the store.dispatch(new SomeAction());
Paul
This is probably related to #28 ...
With Dart 2, this works great:
enum Actions { Increment }
// The reducer, which takes the previous count and increments it in response
// to an Increment action.
int counterReducer(int state, dynamic action) {
if (action == Actions.Increment) {
return state + 0;
}
return state;
}
void main() {
final store = new Store(counterReducer, initialState: 1);
runApp(new MyApp(store: store));
}
class MyApp extends StatelessWidget {
MyApp({this.store});
final Store store;
@OverRide
Widget build(BuildContext context) {
return StoreProvider(
store: store,
child: new MaterialApp(
title: 'Flutter',
home: StoreProvider(
store: store,
child: new Center(
child: new StoreConnector(
converter: (store) => store.state.toString(),
builder: (context, count) {
return new Text('$count');
}))),
),
);
}
}
But the following throws this error:
The following NoSuchMethodError was thrown building StoreConnector(dirty):
The getter '_store' was called on null.
Receiver: null
Tried calling: _store
enum Actions { Increment }
// The reducer, which takes the previous count and increments it in response
// to an Increment action.
int counterReducer(int state, dynamic action) {
if (action == Actions.Increment) {
return state + 0;
}
return state;
}
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
final store = new Store(counterReducer, initialState: 1);
@OverRide
Widget build(BuildContext context) {
return StoreProvider(
store: store,
child: new MaterialApp(
title: 'Flutter',
home: StoreProvider(
store: store,
child: new Center(
child: new StoreConnector(
converter: (store) => store.state.toString(),
builder: (context, count) {
return new Text('$count');
}))),
),
);
}
}
For example, I have the following nested state (only left the relevant parts for clarity):
class AppState {
final OpenBook openBook;
}
class OpenBook {
final bool headerIsFetching;
}
And the reducer:
new AppState(
openBook: new OpenBook(//<- This can be optimized! new instance is built for every cycle regardless of the action
headerIsFetching: buildFetchingReducer<FetchOpenBookHeaderAction, FetchOpenBookHeaderSucceededAction, FetchOpenBookHeaderFailedAction>(state.openBook.headerFetching, action)
)
);
I have thought 2 options:
Do you have a recommendation?
Hi, I am getting an error while trying to install this lib. I don't get the exact reason why this happens.
`[flutter_redux_boilerplate] flutter packages get
Running "flutter packages get" in flutter_redux_boilerplate...
Package flutter_redux has no versions that match 0.4.1 derived from:
I can't seem to get this working. My app crashes right at boot since attempting to upgrade to the latest flutter redux. Below is the currently set up that I am working with.
dependencies:
# Redux
redux: "^3.0.0"
flutter_redux: "^0.5.0"
Flutter 0.3.2 • channel beta • https://github.com/flutter/flutter.git
Framework • revision 44b7e7d3f4 (3 weeks ago) • 2018-04-20 01:02:44 -0700
Engine • revision 09d05a3891
Tools • Dart 2.0.0-dev.48.0.flutter-fe606f890b
The Code Which is Crashing:
final store = new Store<PrimeAppStateModel>(
appReducer,
initialState: new PrimeAppStateModel().initState(), /* initState Returns instance of PrimeAppStateModel */
middleware: [loggingMiddleware]
);
runApp(
new StoreProvider<PrimeAppStateModel>(
store: store,
child: new MaterialApp(
debugShowCheckedModeBanner: _isDebugBannerOn,
localizationsDelegates: [
const HLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: AppSettings.supportedLocales,
onGenerateTitle: (BuildContext context) => HLocalizations.of(context).appName,
color: HColors.canvas,
theme: HTheme.baseTheme(),
home: new AppLaunch()
)
)
);
class AppLaunch extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: HColors.purple,
body: new StoreConnector<PrimeAppStateModel, Store<PrimeAppStateModel>>(
converter: (store) => store,
onInit: (store){
AppEntryModel modalModel = new AppEntryModel.set(
HLocalizations.of(context),
modalMode: ModalMode.login
);
store.dispatch(new AppEntryInitAction(modalModel));
}
builder: (BuildContext build, store) => new Container()
The Error On Build Completion:
I/flutter (20772): :: ACTION LOG :: 2018-05-09 00:35:43.225096: Instance of 'AppEntryInitAction'
I/flutter (20772): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (20772): The following StoreProviderError was thrown building StoreConnector<Store<PrimeAppStateModel>, () =>
I/flutter (20772): void>(dirty):
I/flutter (20772): Error: No StoreProvider<Store<PrimeAppStateModel>> found. To fix, please try:
I/flutter (20772):
I/flutter (20772): * Using Dart 2 (required) by using the --preview-dart-2 flag
I/flutter (20772): * Wrapping your MaterialApp with the StoreProvider<State>, rather than an individual Route
I/flutter (20772): * Providing full type information to your Store<State>, StoreProvider<State> and
I/flutter (20772): StoreConnector<State, ViewModel>
I have checked to make sure that the app is running in Dart 2.0, and it is enabled in Android Studio -> Preferences -> Languages & Frameworks -> Flutter
The launch screen is displayed and then the AppLaunch's StoreConnector runs, since I get a log notice from my logging middleware that store.dispatch(new AppEntryInitAction(modalModel));
action has started. At that point the App Crashes.
I've tried some time trying to isolate where this is happening, and can't seem to pin-point it.
I got this:
I/flutter (15445): The following StoreProviderError was thrown building StoreConnector<AppState, _ViewModel>(dirty):
I/flutter (15445): Error: No StoreProvider<AppState> found. To fix, please try:
I/flutter (15445): * Using Dart 2 (required) by using the --preview-dart-2 flag
I/flutter (15445): * Wrapping your MaterialApp with the StoreProvider<State>, rather than an individual Route
I/flutter (15445): * Providing full type information to your Store<State>, StoreProvider<State> and
I/flutter (15445): StoreConnector<State, ViewModel>
I/flutter (15445): If none of these solutions work, please file a bug at:
I/flutter (15445): https://github.com/brianegan/flutter_redux/issues/new
Steps to reproduce:
AppState
named task_list_redux.dart
, inside of the sandbox package, directory abc
.abc
directory, that builds the StoreConnector, import it like this: import 'package:sandbox/abc/task_list_REDUX.dart';
.Then it fails.
To solve it, simple change the import to import 'task_list_REDUX.dart';
.
I'm repeatedly getting
I/flutter ( 9311): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 9311): The following NoSuchMethodError was thrown building StoreConnector<int, Store<int>>(dirty):
I/flutter ( 9311): The getter 'store' was called on null.
I/flutter ( 9311): Receiver: null
I/flutter ( 9311): Tried calling: store
error when trying to use StoreBuilder
or StoreConnector
in the hierarchy. My app widget builds the rest like this:
@override
Widget build(BuildContext context) {
return new StoreProvider<int>(
store: new Store<int>(
(i, action) => i + 1,
initialState: 0
),
child: new MaterialApp(
title: 'App',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new Scaffold(
appBar: new AppBar(
title: new Text("App"),
),
body: new MainScreen(),
),
);
}
I can't get it to work, I understand StoreProvider
is a simple InheritedWidget, but even if I try to manually call new StoreProvider.of(context)
in a widget down the tree, I get null
. I don't understand, since I've created dead simple inherited widget and placed it instead of StoreProvider
and I could fetch it with of()
method without issues (I almost did a copy-paste from the StoreProvider class).
The int
state and inline reducer is just a result of me trying to simplify the things as much as I can, with no luck. Any idea why would it be like this?
I have a Ticker
streaming events to my store, each of which does some tiny math. At a certain threshold, the app should navigate.
It is mildly complicated to find this event, so I could detect this case and do this within the Ticker
-- though even then, that's kinda gross as I need a context
and I don't actually know how safe it is to just navigate with the last context passed into build()
(since the Ticker
is inherently firing outside the build method and such).
class MyState extends State {
final Store s;
final Context context;
Ticker t = new Ticker((_) {
s.dispatch(...);
if (s.wordPosition + ... > s.???) Navigator.of(context).pushBuilder(...);
});
build(c) {
context = c;
...
}
}
I then figured it'd be better if I have a singleton Stream that can exchange this data. I'm not entirely sure how safe it is in the context of hot reload and such, if I should be pushing out from one Store into a Stream which affects the app elsewhere...but, I figure, that's a small enough concern I may as well do this to unblock me.
reduceEvent(State s, Event e) {
final newPosition = ...;
if (newPosition > s.???) {
s.onDoneSink.add(null);
}
return s.copyWith(....);
}
I realized that I have the same problem of getting the context -- I could use a StreamBuilder, but that would always, on each build, get me the most recent navigation pushed to my stream. Not what I want. And if I don't use StreamBuilder, I'm not a builder, and so I don't have a context to go off of.
class MyApp {
Stream s;
build() => new StreamBuilder(s, (context, _, currentValue) {
if (currentValue) // the last value is irrelevant to whether we should navigate right now
Navigator.of(context).pushBuilder(...);
return new Container();
};
I'm not convinced this shouldn't be a middleware, but they don't seem well documented. I think navigation is often done in a middleware with react/redux, but don't actually follow the code examples well, and they lean heavily on the navigation APIs itself which are different on the web.
I'm starting to realize, anyways, that Navigator is probably basically just altering the state of the MaterialApp
by probably looking up some kind of InheritedNavigationWidget
or something
class Navigator extends InheritedWidget {
Stack<String> routes;
}
class MaterialApp {
build(context) =>
Navigator.of(context).buildCurrentRoute();
}
So this makes me think I shouldn't be navigating at all, but that my reducer should set page = Pages.Histogram
in my state, and then my MyApp
component can do all routing with that. Downside here is that I have to reimplement things like push/pop/replace.
build() =>
new MaterialApp(
child: new StoreConnector(
store: s,
converter: getRoute,
builder: (_, route) {
switch (route) {
...
}
}
)
);
Is this a solved problem, am I on the right track that I should just reimplement navigation in redux because navigation is just a form of state management anyway built through less testable techniques?
Hello,
This one is a doozy - strap yourself in!
I just found something the (super) hard way, and I want to record my thoughts before I leave work for the day. If necessary, I'm happy to put more effort in here and try to produce a succinct repro. Right now I need some validation/feedback on whether I'm doing something obviously wrong.
I was writing an automatic logout feature for my app, so if the user stops interacting for, say, 30 seconds, it logs them out. To achieve this, I wrote an InputDetector
control like this:
class InputDetector extends StatefulWidget {
final VoidCallback onInput;
final Widget child;
InputDetector({@required this.child, @required this.onInput})
: assert(child != null),
assert(onInput != null);
@override
State<StatefulWidget> createState() => new _InputDetectorState();
}
class _InputDetectorState extends State<InputDetector> {
@override
void initState() {
super.initState();
// Detect raw input from the keyboard (alas, only the hardware keyboard).
RawKeyboard.instance.addListener(_onRawKeyEvent);
}
@override
void dispose() {
RawKeyboard.instance.removeListener(_onRawKeyEvent);
super.dispose();
}
@override
Widget build(BuildContext context) {
var touchDetector = new Listener(
behavior: HitTestBehavior.translucent,
child: widget.child,
onPointerDown: (_) => widget.onInput(),
onPointerMove: (_) => widget.onInput(),
onPointerCancel: (_) => widget.onInput(),
onPointerUp: (_) => widget.onInput(),
);
return touchDetector;
}
void _onRawKeyEvent(RawKeyEvent event) => widget.onInput();
}
It works fine, apart from not being able to detect software keyboard input (but I'm chasing that up separately).
Anyway, I hook it into the root of my visual tree, and then dispatch an action whenever input is detected:
var inputDetector = new InputDetector(
child: materialApp,
onInput: () => store.dispatch(new DeferIdleLogout()),
);
My middleware then uses these actions to manage a timer, which will log the user out if it expires. I added a global flag to enable/disable that particular middleware, since it would be super annoying for developers to be logged out all the time whilst working on the app. The CI server substitutes in an appropriate value for the global prior to building, so there's no chance of developers accidentally disabling the feature.
This was all working wonderfully, except some of my widget tests started to fail. I dug in and found that all tests performing swipe interactions were failing. Weird. I tried running up the app and, sure enough, I couldn't swipe my Dismissible
widgets any more. Wat.
After initially suspecting flutter was to blame - something to do with using a Listener
above a Dismissible
, I quickly dismissed this when an attempted repro worked fine. I had to whittle things down, control by control, line by line. It took quite a while, but I eventually narrowed it down to this code from above:
var inputDetector = new InputDetector(
child: materialApp,
onInput: () => store.dispatch(new DeferIdleLogout()),
);
Specifically, dispatching the action. If I commented out the dispatch and did nothing, it all worked fine.
Digging even further, I found that if I only forwarded onPointerCancel
and onPointerUp
, it worked. Forwarding either onPointerDown
or onPointerMove
exhibited the same problem. Now I started to suspect the frequency of the action dispatches or something. But then I tried only onPointerDown
and it still didn't work. Wowsers. Now I started thinking maybe dispatching the action at the start of the interaction was somehow thwarting the interaction, even though nothing was handling that action.
Wait, I thought, nothing is handling the action. Maybe that's relevant (and maybe the issue title was a total spoiler). I enabled the middleware that dealt with this action (even though I don't ordinarily want it enabled for developers), and sure enough, it worked. Even with all input events forwarded, things were working fine.
So, um, sorry for the short story, but I'm really confused. I can see an easy way around this: instead of selectively registering the middleware, always register it and check the global value when handling the action. But I don't understand why this is happening. If an action has no reducer/middleware anywhere, would you expect Weird Things like this to occur?
Thanks for reading.
First of all, thanks for putting the effort to release a version of flutter_redux
that support Dart 2 this quickly, really appreciated 💯
Now, the issue: In the repo it looks like the latest version of the package that's been released is 0.4.0
but actually in pub it got released as 0.3.6
https://pub.dartlang.org/packages/flutter_redux/versions/0.3.6
Version 0.3.6
of the flutter_redux
package seems to work well with Dart 2, so It should probably be deleted and re-released as 0.4.0, as people can get confused as to which version to use for Dart 1 and Dart 2.
Hi,
Great library, I've really enjoyed implementing it but I've had a bit of a mammoth session and can't seem to wrap my head around this one. I am pretty new to Flutter and Dart so apologies if this is a simple question.
I want to use a pull to refresh which sends an action and retrieves a new set of items from the web via the middleware. Doing this without the refresh indicator works well, but the indicator needs a Future passed to it that resolves when the action has finished. Is there any way that I can use the existing stock widget to start and stop based on actions?
Thanks
Sorry, me again :)
Another question: what would be the correct way to reset some state prior to entering a given view? Imagine a form in the app that should reset whenever the user starts it. But it's a multi-page form, so they can also return to the start via the back button (which should not reset the data). Therefore, I need to send a ResetAction
during the init
stage of the page.
However, achieving this seems problematic. The only way I know of obtaining the current store is:
new StoreProvider<AppState>.of(context).store
But this doesn't work during init
:
The following assertion was thrown building _ModalScopeStatus(active, can pop):
I/flutter (13145): inheritFromWidgetOfExactType(StoreProvider) was called before
I/flutter (13145): _XXX.initState() completed.
I/flutter (13145): When an inherited widget changes, for example if the value of Theme.of() changes, its dependent
I/flutter (13145): widgets are rebuilt. If the dependent widget's reference to the inherited widget is in a constructor
I/flutter (13145): or an initState() method, then the rebuilt dependent widget will not reflect the changes in the
I/flutter (13145): inherited widget.
I/flutter (13145): Typically references to to inherited widgets should occur in widget build() methods. Alternatively,
I/flutter (13145): initialization based on inherited widgets can be placed in the didChangeDependencies method, which
I/flutter (13145): is called after initState and whenever the dependencies change thereafter.
I don't think didChangeDependencies
is what I want, because then the data could reset in other circumstances.
I also considered having the reset just before the code that routes the user to the page. That totally works, and is what I've been running with for a while (and will have to continue doing so for now). However, I was trying to move away from this because it means one must remember to dispatch the action before routing. When you have multiple parts of the application routing to that same page, each of those parts must remember (or the routing must be abstracted/extracted in some fashion).
Any thoughts on this?
Need some help figuring out how to do routing with StoreConnectors.
Created a basic app with navigation to 2 screens.
Screen 2 is uses a StoreConnector while Screen3 does not. Navigation to Screen 2 fails while getting the store.
Here is the call stack of the error
The following NoSuchMethodError was thrown building StoreConnector<dynamic, Store>(dirty):
The getter 'store' was called on null.
Receiver: null
Tried calling: store
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/dart:core/object_patch.dart:46)
#1 StoreConnector.build (package:flutter_redux/flutter_redux.dart:156:44)
#2 StatelessElement.build (package:flutter/src/widgets/framework.dart:3655:28)
#3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3602:15)
The route for Screen2 looks like this
routes: <String, WidgetBuilder>{
'/screen2': (BuildContext context) => new StoreBuilder( builder: (context, store) {
return new Screen2();
}),
Below is the complete code.
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';
dynamic reducer(dynamic state, action) {
return state;
}
void main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
final store = new Store(reducer, initialState: {"1" : 1, "2": 2});
Widget build(BuildContext context) {
return new MaterialApp(
home: new StoreProvider(
store: store,
child: new Screen1(),
),
routes: <String, WidgetBuilder>{
'/screen2': (BuildContext context) => new StoreBuilder( builder: (context, store) {
return new Screen2();
}),
'/screen3': (BuildContext context) => new Screen3()
},
);
}
}
class Screen1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold( // 1
appBar: new AppBar(
title: new Text("Screen 1"), // screen title
),
body: new StoreConnector<dynamic, Map>(
converter: (store) => (store.state['1']),
builder: (context, jsonNodeData) => new Center(
child: new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new RaisedButton(
onPressed:(){
Navigator.of(context).pushNamed("/screen2");
},
child: new Text("Go to Screen 2"),
),
new RaisedButton(
onPressed:(){
Navigator.of(context).pushNamed("/screen3");
},
child: new Text("Go to Screen 3"),
),
]
)
)
)
);
}
}
class Screen2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Screen 2"),
),
body: new StoreConnector<dynamic, Map>(
converter: (store) => (store.state['1']),
builder: (context, jsonNodeData) => new Text("Screen 2")
) ,
);
}
}
class Screen3 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Screen 3"),
),
body: new Text("Screen 3")
);
}
}
Hi,
There's something I've found myself doing in each of my views, and I can't help but think it's more complicated than it should be, so I just wanted to check in to see how I should be handling this.
The scenario is simply that I want to transfer data from the VM to the view for a TextField
that has a controller. The reasons I might need to do this include:
As a simple example, imagine a login page. When login fails, the reducer resets the password back to empty (or null
). That should of course be reflected in the view. However, I don't see a way to ensure this without manually applying the value to the TextEditingController
.
Manually transferring the values isn't so bad, except that it quickly gets complicated because I need to take selection into account. Currently my helper method looks like this:
void _updateText(TextEditingController controller, String text) {
text = text ?? "";
var selection = controller.selection;
controller.text = text;
if (selection.start > text.length || selection.end > text.length) {
return;
}
controller.selection = selection;
}
I call this at the start of my build
method for each TextEditingController
, passing in the value provided by the VM. It's all a bit awkward, so I feel like I'm missing something obvious here.
Any thoughts?
Error creating widget with fluro:
I/flutter ( 6397): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 6397): The following StoreProviderError was thrown building StoreConnector<AppState,
I/flutter ( 6397): BangumiListState>(dirty):
I/flutter ( 6397): Error: No StoreProvider found. To fix, please try:
I/flutter ( 6397):
I/flutter ( 6397): * Using Dart 2 (required) by using the --preview-dart-2 flag
I/flutter ( 6397): * Wrapping your MaterialApp with the StoreProvider, rather than an individual Route
I/flutter ( 6397): * Providing full type information to your Store, StoreProvider and
I/flutter ( 6397): StoreConnector<State, ViewModel>
I/flutter ( 6397):
I/flutter ( 6397): If none of these solutions work, please file a bug at:
I/flutter ( 6397): https://github.com/brianegan/flutter_redux/issues/new
I/flutter ( 6397):
I/flutter ( 6397): When the exception was thrown, this was the stack:
I/flutter ( 6397): #0 StoreProvider.of (package:flutter_redux/flutter_redux.dart:29:27)
I/flutter ( 6397): #1 StoreConnector.build (package:flutter_redux/flutter_redux.dart:184:28)
I/flutter ( 6397): #2 StatelessElement.build (package:flutter/src/widgets/framework.dart:3687:28)
I/flutter ( 6397): #3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3634:15)
I/flutter ( 6397): #4 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #5 StatelessElement.update (package:flutter/src/widgets/framework.dart:3694:5)
I/flutter ( 6397): #6 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #7 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #8 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #9 StatelessElement.update (package:flutter/src/widgets/framework.dart:3694:5)
I/flutter ( 6397): #10 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #11 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #12 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #13 ProxyElement.update (package:flutter/src/widgets/framework.dart:3901:5)
I/flutter ( 6397): #14 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #15 RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:4371:32)
I/flutter ( 6397): #16 MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4761:17)
I/flutter ( 6397): #17 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #18 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #19 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #20 ProxyElement.update (package:flutter/src/widgets/framework.dart:3901:5)
I/flutter ( 6397): #21 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #22 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #23 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #24 ProxyElement.update (package:flutter/src/widgets/framework.dart:3901:5)
I/flutter ( 6397): #25 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #26 RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:4371:32)
I/flutter ( 6397): #27 MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4761:17)
I/flutter ( 6397): #28 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #29 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #30 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #31 StatefulElement.update (package:flutter/src/widgets/framework.dart:3791:5)
I/flutter ( 6397): #32 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #33 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #34 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #35 ProxyElement.update (package:flutter/src/widgets/framework.dart:3901:5)
I/flutter ( 6397): #36 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #37 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #38 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #39 StatefulElement.update (package:flutter/src/widgets/framework.dart:3791:5)
I/flutter ( 6397): #40 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #41 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4653:14)
I/flutter ( 6397): #42 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #43 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #44 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #45 StatelessElement.update (package:flutter/src/widgets/framework.dart:3694:5)
I/flutter ( 6397): #46 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #47 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4653:14)
I/flutter ( 6397): #48 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #49 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #50 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #51 StatefulElement.update (package:flutter/src/widgets/framework.dart:3791:5)
I/flutter ( 6397): #52 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #53 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #54 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #55 StatefulElement.update (package:flutter/src/widgets/framework.dart:3791:5)
I/flutter ( 6397): #56 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #57 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #58 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #59 ProxyElement.update (package:flutter/src/widgets/framework.dart:3901:5)
I/flutter ( 6397): #60 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #61 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #62 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #63 ProxyElement.update (package:flutter/src/widgets/framework.dart:3901:5)
I/flutter ( 6397): #64 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #65 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #66 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #67 StatefulElement.update (package:flutter/src/widgets/framework.dart:3791:5)
I/flutter ( 6397): #68 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #69 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #70 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #71 StatelessElement.update (package:flutter/src/widgets/framework.dart:3694:5)
I/flutter ( 6397): #72 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #73 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #74 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #75 StatelessElement.update (package:flutter/src/widgets/framework.dart:3694:5)
I/flutter ( 6397): #76 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #77 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4653:14)
I/flutter ( 6397): #78 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #79 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4653:14)
I/flutter ( 6397): #80 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #81 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4653:14)
I/flutter ( 6397): #82 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #83 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #84 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #85 StatefulElement.update (package:flutter/src/widgets/framework.dart:3791:5)
I/flutter ( 6397): #86 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #87 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #88 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #89 StatefulElement.update (package:flutter/src/widgets/framework.dart:3791:5)
I/flutter ( 6397): #90 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #91 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4653:14)
I/flutter ( 6397): #92 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #93 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #94 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #95 ProxyElement.update (package:flutter/src/widgets/framework.dart:3901:5)
I/flutter ( 6397): #96 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #97 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4653:14)
I/flutter ( 6397): #98 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #99 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #100 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #101 StatefulElement.update (package:flutter/src/widgets/framework.dart:3791:5)
I/flutter ( 6397): #102 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #103 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #104 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #105 StatelessElement.update (package:flutter/src/widgets/framework.dart:3694:5)
I/flutter ( 6397): #106 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #107 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4653:14)
I/flutter ( 6397): #108 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #109 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #110 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #111 ProxyElement.update (package:flutter/src/widgets/framework.dart:3901:5)
I/flutter ( 6397): #112 Element.updateChild (package:flutter/src/widgets/framework.dart:2691:15)
I/flutter ( 6397): #113 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3645:16)
I/flutter ( 6397): #114 Element.rebuild (package:flutter/src/widgets/framework.dart:3487:5)
I/flutter ( 6397): #115 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2234:33)
I/flutter ( 6397): #116 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:626:20)
I/flutter ( 6397): #117 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:208:5)
I/flutter ( 6397): #118 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:990:15)
I/flutter ( 6397): #119 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:930:9)
I/flutter ( 6397): #120 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.scheduleWarmUpFrame. (package:flutter/src/scheduler/binding.dart:751:7)
I/flutter ( 6397): #122 _Timer._runTimers (dart:isolate/runtime/libtimer_impl.dart:382:19)
I/flutter ( 6397): #123 _Timer._handleMessage (dart:isolate/runtime/libtimer_impl.dart:416:5)
I/flutter ( 6397): #124 _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:165:12)
I/flutter ( 6397): (elided one frame from package dart:async)
I/flutter ( 6397): ════════════════════════════════════════════════════════════════════════════════════════════════════
class MyApp extends StatelessWidget {
final Store<AppState> _store = new Store<AppState>(appReducer);
@override
Widget build(BuildContext context) {
return new StoreProvider<AppState>(
store: _store,
child: new MaterialApp(
theme: new ThemeData(
primarySwatch: Colors.blue,
scaffoldBackgroundColor: Colors.white,
),
onGenerateRoute: initRouterGenerator(),
),
);
}
}
Hi,
First of all, thank you guys for writing such a great library; I am still learning Redux but this package has been a wonderful first introduction.
I am trying to use the google_sign_in package in order to sign users into my app, and it worked fine before I moved to Redux. However, it now throws an error any time I try to sign in to the app. The sign in box that normally pops up now does not, so I think that it might have to do with trying to render the sign in box from Middleware, which doesn't really have a context (?)
Here is the associated code:
SignIn
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:kickit/data/injectors.dart';
import 'package:kickit/data/profile_store.dart';
import 'package:kickit/database/networking.dart';
import 'package:kickit/database/profile_package.dart';
/// A collection of methods that handle the signing in and signing out of the
/// current user.
abstract class ISignIn {
/// Signs in the current user, returning a [ProfilePackage] containing the
/// current user's data.
Future<ProfilePackage> signIn();
/// Signs the current user out of Firebase and Google.
Future<Null> signOut();
/// Signs the current user out of Firebase and Google and deletes their profile
/// from the database.
Future<Null> signOutAndDelete(String uid);
}
/// Performs sign in operations with real network data.
class SignIn extends ISignIn {
final IProfileStore store;
SignIn() : store = new ProfileInjector().profileLoader;
Future<ProfilePackage> signIn() async {
// Check to see if there is currently a signed in user.
final GoogleSignInAccount account = googleSignIn.currentUser;
// Try to sign in without prompting the user.
if (account == null) {
account == await googleSignIn.signInSilently();
}
// If this doesn't work, prompt the user to sign in.
if (account == null) {
account == await googleSignIn.signIn();
}
// If this doesn't work, throw an error that should tell the user that
// they must sign in.
if (account == null) {
throw new StateError("The user must log in.");
}
final GoogleSignInAuthentication auth = await account.authentication;
// Authenticate the user with firebase.
final FirebaseUser user = await firebaseAuth.signInWithGoogle(
idToken: auth.idToken, accessToken: auth.accessToken);
if (user == null || user.isAnonymous) {
throw new StateError("Log in error.");
}
final ProfilePackage package = await store.loadProfile(user.uid);
// No user was found, so create a new one and save it to the database.
if (package == null) {
ProfilePackage newPackage =
new ProfilePackage.fromGoogleSignIn(account, user);
await store.saveProfile(newPackage);
return newPackage;
} else {
return package;
}
}
Future<Null> signOut() async {
await googleSignIn.signOut();
await firebaseAuth.signOut();
}
Future<Null> signOutAndDelete(String uid) async {
await store.deleteProfile(uid);
await signOut();
}
}
/// Performs sign in operations with mock data.
class MockSignIn implements ISignIn {
@override
Future<ProfilePackage> signIn() {
return new Future.delayed(delayMedium,
() => new ProfilePackage("_", "Jaewon Yang", "_", "Test profile."));
}
@override
Future<Null> signOut() {
return null;
}
@override
Future<Null> signOutAndDelete(String uid) {
return null;
}
}
Middleware
import 'package:kickit/actions/sign_in_actions.dart';
import 'package:kickit/data/injectors.dart';
import 'package:kickit/data/sign_in.dart';
import 'package:kickit/models/app_state.dart';
import 'package:kickit/models/profile.dart';
import 'package:kickit/utils/values/internal_strings.dart';
import 'package:kickit/utils/values/keys.dart';
import 'package:redux/redux.dart';
/// Creates all the [Middleware] associated with signing the user in and out.
List<Middleware<AppState>> createSignInMiddleware() {
final ISignIn signIn = new SignInInjector().signIn;
final Middleware<AppState> mSignIn = _createSignIn(signIn);
final Middleware<AppState> mSignOut = _createSignOut(signIn);
final Middleware<AppState> mSignOutAndDelete =
_createSignOutAndDelete(signIn);
return combineTypedMiddleware(
[
new MiddlewareBinding<AppState, SignInAction>(mSignIn),
new MiddlewareBinding<AppState, SignOutAction>(mSignOut),
new MiddlewareBinding<AppState, SignOutAndDeleteAction>(
mSignOutAndDelete),
],
);
}
/// Creates the [Middleware] associated with a sign in attempt.
Middleware<AppState> _createSignIn(ISignIn signIn) {
return (Store<AppState> store, action, NextDispatcher next) {
signIn.signIn().then(
(user) {
store.dispatch(
new SignedInAction(
new Profile.fromPackage(user),
),
);
Keys.navigatorKey.currentState
.pushReplacementNamed(InternalStrings.mainScreenRoute);
},
).catchError((_) => store.dispatch(new FailedSignInAction()));
next(action);
};
}
/// Creates the [Middleware] associated with a sign out attempt.
Middleware<AppState> _createSignOut(ISignIn signIn) {
return (Store<AppState> store, action, NextDispatcher next) {
signIn.signOut().then(
(_) {
store.dispatch(new SignedOutAction());
Keys.navigatorKey.currentState
.pushReplacementNamed(InternalStrings.splashScreenRoute);
},
);
next(action);
};
}
/// Creates the [Middleware] associated with a sign out and delete attempt.
Middleware<AppState> _createSignOutAndDelete(ISignIn signIn) {
return (Store<AppState> store, action, NextDispatcher next) {
signIn.signOutAndDelete(store.state.profile.uid).then(
(_) {
store.dispatch(new SignedOutAction());
Keys.navigatorKey.currentState
.pushReplacementNamed(InternalStrings.splashScreenRoute);
},
);
next(action);
};
}
Any help would be appreciated!
I'm following the tutorial from https://flutterbyexample.com/redux-app-getting-to-start
However, when I hot reload, the UI does not update with the new value. This issue is same as #42 . Btw, hot reload works fine on other projects without using redux. Another interesting thing is that after hot reloading, the logger middleware prints the same message twice for a single tap event.
Here is the output from flutter doctor
:
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel beta, v0.4.4, on Microsoft Windows [Version 10.0.17134.112], locale en-US)
[√] Android toolchain - develop for Android devices (Android SDK 26.0.2)
[√] Android Studio (version 2.3)
X Flutter plugin not installed; this adds Flutter specific functionality.
X Dart plugin not installed; this adds Dart specific functionality.
[!] IntelliJ IDEA Ultimate Edition (version 2017.1)
X Flutter plugin not installed; this adds Flutter specific functionality.
X Dart plugin not installed; this adds Dart specific functionality.
[√] VS Code, 32-bit edition (version 1.23.1)
[√] Connected devices (1 available)
! Doctor found issues in 1 category.
I've attached the contents of lib folder and pubspec.yaml
firebaseredux.zip
Hi,
I have this pattern where I initially injected a store instance in all my components using it (I wasn't aware of new StoreProvider.of(...)
).
In my App, in some places, I also do 1 time initializations (ie: fetch data via store.dispatch(...)
) in initState()
. Previously, I could do it fine, because I had an instance of my Store.
Now I tried to move towards to new StoreProvider.of(...)
, but I faced the issue where context.inheritFromWidgetOfExactType(StoreProvider)
can't be called in initState
. Is there a way to achieve that somehow?
Seems like the Flutter docs suggest that I use didChangeDependencies()
but that is not called only 1 time.
Thanks
I read issue #46 and that does seem related to my issue however how there is a bit more in my case.
I took a stateless widget and wrapped the Store Provider in it and attached the Reducer to this widget as a static function. When I tried to wrap the root widget in this stateless widget it gave me the "can't find store provider" error like in #46. If I transplant the Store Provider from the stateless widget and directly put it into my root widget, I am unable to gain access to my reducer (I took the reducer out of the wrapper stateless widget and just made it a normal top level function). I happened to leave the wrapper widget code mostly alone (aside from removing the reducer from it) but its not attached to anything and it finds the reducer when it is passed into the Store Provider there.
On the other hand when I pass the same reducer into the Store Provider in the root widget it gives me this very bizarre error: the argument type '(AppState, dynamic) → AppState can't be assigned to the parameter type '(AppState, dynamic) → AppState
Any Ideas of what exactly is going on here? This is a really confusing error and even taking the reducer and its logic and putting it directly into my main file does not solve this problem.
I tried to get the flutter-redux example working with the Dart 2 preview, but I get the following error when I try to run it:
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building StoreConnector<int, String>(dirty):
The getter 'store' was called on null.
Receiver: null
Tried calling: store
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core/object_patch.dart:46:5)
#1 StoreConnector.build (package:flutter_redux/flutter_redux.dart:156:44)
#2 StatelessElement.build (package:flutter/src/widgets/framework.dart:3678:28)
Works fine when not activating the preview. The Flutter build I'm using is:
Flutter 0.1.5 • channel beta • https://github.com/flutter/flutter.git
Framework • revision 3ea4d06340 (3 weeks ago) • 2018-02-22 11:12:39 -0800
Engine • revision ead227f118
Tools • Dart 2.0.0-dev.28.0.flutter-0b4f01f759
I'm trying to implement this idea where I dispatch a log-in action and from the log-in middleware, launch another action that changes a variable in the appState if there's an error in the log in process. That's done.
The issue is, I tried to use the storeConnector, since it updates the application based on appState changes, to check for the change and open a SnackBar when the variable changes as shown in the code for the container below, but I'm obviously not doing this the right way:
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:redux/redux.dart';
import 'package:takeout/models/app_state.dart';
import 'package:takeout/presentation/login_form.dart';
import 'package:takeout/actions/auth_actions.dart';
class AuthLogin extends StatelessWidget {
AuthLogin({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
// String isLoginFailed = StoreProvider.of<AppState>(context).state.isLoginFailed;
return new StoreConnector<AppState, _ViewModel>(
distinct: true,
converter: _ViewModel.fromStore,
builder: (BuildContext context, _ViewModel vm){
// if (!StoreProvider.of<AppState>(context).reducer == "") {
// Scaffold.of(context).showSnackBar(new SnackBar(
// backgroundColor: Colors.red,
// content: new Text("Error Logging In")));
// //put reset state.isLoginFailed action here
// isLoginFailed = StoreProvider.of<AppState>(context).state.isLoginFailed;
// }
return new LoginForm(
email: vm.email,
password: vm.password,
saveEmail: vm.saveEmail,
savePassword: vm.savePassword,
attemptLogin: vm.attemptLogin,
);
}
);
}
}
class _ViewModel {
// It should take in whatever it is you want to 'watch'
final Function saveEmail;
final Function savePassword;
final Function attemptLogin;
final String email;
final String password;
_ViewModel({
this.email,
this.password,
this.saveEmail,
this.savePassword,
this.attemptLogin
});
// This is simply a constructor method.
// This creates a new instance of this _viewModel
// with the proper data from the Store.
static _ViewModel fromStore(Store<AppState> store) {
return new _ViewModel(
email: store.state.typedEmail,
password: store.state.typedPassword,
///...
saveEmail: (val){
store.dispatch(new SaveEmail(val));
},
savePassword: (val){
store.dispatch(new SavePassword(val));
},
attemptLogin: (){
store.dispatch(
new LogInAction(store.state.typedEmail, store.state.typedPassword)
);
}
);
}
}
I'm sorry if this is such a tyro question but I'm just getting started with Redux and I still dont know how everything works out yet. The commented part is the part I tried to implement the "listen" for the appState change.
Been thinking about this for a while now and it's been mind boggling coming up with how to implement automatically searching when a user types with flutter_redux.
I found this repo https://github.com/brianegan/dart_redux_epics and I feel like this is the correct way to do it after checking out the readme file.
My questions are:
Thank you.
In the following code, I use the CopyWith pattern. One problem I'm experiencing with this is if I want to set a property to a null value (this is quite easy using the spread operator in js). The value of the name field of the 'Joe' object below is not null, it is 'Bob'. Do you know of any way to overcome this problem in Dart?
void main() {
var bob = new Person(age: 15, name: 'Bob');
print(bob.toString());
var joe = bob.copyWith(name: null);
print(joe.toString());
}
class Person {
final int age;
final String name;
Person({this.age, this.name});
Person copyWith({int age, String name}) {
return new Person(
age: age ?? this.age,
name: name ?? this.name,
);
}
}
it 's allright before hot reload, model has been updated, but widget didn't update when i did a hot reload
Hey @brianegan
first of all thanks for the awesome library!!!
I noticed the following:
Im having an app state requestLessonState
which is on app launch null
. onInit
I would like to dispatch an action to assign requestLessonState
a value, in this case false
. The action get dispatched as expected and the state changed to the expected value. BUT the view model get generated before the state changed. So for me it is not possible to basically create an init value for the model.
Is this an issue or an expected behaviour (something async or so)? If it is an expected behaviour, do you something in mind to solve this "issue"?
I hope you can help me some how or push me in the right direction.
Thanks in advance
@immutable
class AppState {
final RequestLessonState requestLessonState;
AppState({
this.requestLessonState,
});
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is AppState &&
runtimeType == other.runtimeType &&
requestLessonState == other.requestLessonState;
@override
int get hashCode => requestLessonState.hashCode;
}
@immutable
class RequestLessonState {
final bool dropOffIsDifferent;
RequestLessonState({
this.dropOffIsDifferent,
});
RequestLessonState copyWith({
String dropOffLocation,
}) {
return new RequestLessonState(
dropOffIsDifferent: dropOffIsDifferent ?? this.dropOffIsDifferent,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is RequestLessonState &&
runtimeType == other.runtimeType &&
dropOffIsDifferent == other.dropOffIsDifferent;
@override
int get hashCode => dropOffIsDifferent.hashCode;
}
class CreateLessonScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new StoreConnector<AppState, _ViewModel>(
onInit: (store) => store.dispatch(new InitRequestLessonAction()),
distinct: true,
converter: _ViewModel.fromStore,
builder: (context, vm) {
return new Scaffold(
appBar: new AppBar(),
body: new SafeArea(
bottom: true,
child: new Column(
children: <Widget>[
new Switch(value: vm.dropOfIsDifferent, onChanged: (value){}),
],
),
),
);
},
);
}
}
class _ViewModel {
final bool dropOfIsDifferent;
_ViewModel({
this.dropOfIsDifferent,
});
static _ViewModel fromStore(Store<AppState> store) {
return new _ViewModel(
dropOfIsDifferent: store.state.requestLessonState.dropOffIsDifferent,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is _ViewModel &&
runtimeType == other.runtimeType &&
dropOfIsDifferent == other.dropOfIsDifferent;
@override
int get hashCode => dropOfIsDifferent.hashCode;
}
class InitRequestLessonAction {
InitRequestLessonAction();
@override
String toString() {
return 'InitRequestLessonAction{}';
}
}
// the main reducer
AppState appReducer(AppState state, action) {
return new AppState(
requestLessonState: requestLessonReducer(state.requestLessonState, action),
);
}
final requestLessonReducer = combineTypedReducers<RequestLessonState>([
new ReducerBinding<RequestLessonState, InitRequestLessonAction>(_setInitRequestLessonAction),
]);
RequestLessonState _setInitRequestLessonAction(RequestLessonState state, InitRequestLessonAction action) {
return new RequestLessonState(dropOffIsDifferent: false);
}
Hi Brian,
I'm trying to build a small chat app with Redux, and when new list items (chat messages) are added to the store I want to animate a scroll down to the bottom of the list so they are in view.
Without Redux, in Flutter you're able to use ScrollController then call _scrollController.animateTo
when you call setState
to add the new items (source).
In React, you would have componentDidUpdate
, so you would be able to animate the scroll to the bottom in componentDidUpdate
when your Redux store changes (source).
Wondering what is the equivalent way to do this in Redux with Flutter since StatelessWidget
doesn't provide hooks like componentDidUpdate
? Unfortunately, just calling _scrollController.animateTo
after dispatching a Redux action doesn't scroll to the bottom position after the Redux store updates.
I keep getting not storeprovider found. here is my main.dart code:
`
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
final store = new Store<AppState>(
appReducer,
initialState: new AppState.loading(),
);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return new StoreProvider<AppState>(
store: store,
child: new MaterialApp(
title: 'Flutter Demo',
routes: <String, WidgetBuilder>{
FamilRoutes.home: (context) {
return new StoreBuilder(
onInit: (store) =>
store.dispatch(new LoadAreasAction()),
builder: (context, store) {
return new ListAreas();
});
},
FamilRoutes.addArea: (context) {
return new AddArea();
},
}
),
);
}
}`
Here is my package versions:
google_sign_in: "^3.0.3+1"
firebase_auth: "^0.5.10"
cloud_firestore: "^0.7.2"
redux: "^3.0.0"
flutter_redux: "^0.5.1"
The screen is stateless, I am storing widget states in the Store.
The docstring for StoreProvider states -
Provides a Redux [Store] to all ancestors of this Widget. This should generally be a root widget in your App. Connect to the Store provided by this Widget using a [StoreConnector] or [StoreBuilder].
It should be descendants and not ancestors.
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.