Comments (12)
Hey hey :) Trip was great, thanks! And apologies -- somehow missed this one!
Overall, I'm a bit confused by this line, so my advice not be amazing: "I want some methods inside my Widgets to dispatch actions to the Store from the StoreProvider, but not necessarily build a widget." If you've got methods inside a Widget, I guess I'm thinking you'd need to instantiate / build it somewhere?
Here are a couple options I could think of:
- Use the
StoreConverter
to create aViewModel
that contains functions that dispatch actions to the store for ya. For example, you can create aStoreConnector
with aconverter
that returns aFunction()
. You can then pass this function to the Widget that needs to dispatch an action. Examples can be seen here:
- Only returns a function that dispatches an action: https://github.com/brianegan/flutter_architecture_samples/blob/master/example/redux/lib/containers/add_todo.dart
- Returns a ViewModel, with some data AND a function that dispatches an action: https://github.com/brianegan/flutter_architecture_samples/blob/master/example/redux/lib/containers/todo_details.dart - Pass
Store
as a constructor property directly to the Widget which has methods that need to call the Store. Something like:MyWidget(store: StoreProvider.of<AppState>())
. Then you can use thestore
within the methods of that class or it's State class. - Create a Singleton that holds the Store and provides static access to the Store (note, this might not be the best idea, as it couples your Widget to the Singleton and can make testing harder). You can follow this example of how to create a Singleton that could hold the Store: https://stackoverflow.com/questions/12649573/how-do-you-build-a-singleton-in-dart
Do any of those help? If not, please feel free to write back, and it'd be great if you can describe a use-case, and I'll try to help further :)
from flutter_redux.
My favorite solution.
void _onEditNote() { final store = StoreProvider.of<AppState>(context); store.dispatch(NoteEditAction(note)); }
from flutter_redux.
Thanks @jeroen-meijer, that definitely helps me understand.
In this case, I'd actually recommend a slightly different path: Middleware functions should generally handle async calls in Redux. More docs: https://github.com/johnpryan/redux.dart/blob/master/doc/async.md
In this case, you'll want to create a Middleware function that will listen forLoadUserAction
s that are dispatched. When a LoadUserAction
is dispatched, it will call the database layer for ya, and transform the result of that async call into new success / fail actions.
You can provide a function to your HomeScreen
that will dispatch a LoadUserAction
using a StoreConnector
.
(Also, random side fact: your ExampleSpecialStoreProvider
is the same thing as a StoreBuilder
provided by this lib if you ever need that functionality again!).
Example adapted to use Middleware:
// Actions
class LoadUserSuccessAction {AssignUserByDocument(dynamic document);}
class LoadUserFailedAction {}
// Also Introduce Loading Action
class LoadUserAction(
final String id;
LoadUserAction(this.id);
);
// Create a Middleware
void loadUserMiddleware(Store<AppState> store, dynamic action, NextDispatcher next) {
if (action is LoadUserAction) {
Database.getDocument("user").byId(action.id)
.then((document) => store.dispatch(LoadUserSuccessAction(document))
.catchError((error) => store.dispatch(LoadUserFailedAction());
}
next(action);
}
AppState exampleReducer(AppState prev, dynamic action) { return prev; }
// Create your store with your middleware
final store = new Store<AppState>(exampleReducer, middleware: [loadUserMiddleware]);
class HomeScreen extends StatelessWidget {
final Function() onButtonPressed;
HomeScreen({Key key, @required this.onButtonPressed}): super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: MaterialButton(
onPressed: onButtonPressed,
child: Text("Initialize user object from DB!"),
),
),
);
}
}
// When instantiating the HomeScreen...
StoreConnector<AppState, Function()>(
converter: (store) => store.dispatch(LoadUserAction("12345")),
builder: (context, fn) => HomeScreen(onButtonPressed: fn),
);
Hope that helps!
from flutter_redux.
Nevermind. It's global in stateful widgets. I'm an idiot today I guess.
from flutter_redux.
@jeroen-meijer Did I totally blow your mind, or is that working out for ya? Let me know, or if it's safe to close this out now :)
from flutter_redux.
on a similar question, how to I get store instance outside of a widget scope? currently I just save the store instance as a global static variable, however this approach feel very much like a hack. to explain the usage, consider I have a local service like component (one that listen to app lifecycle or network connectivity), which I'd like to have the ability to actively dispatch events such as onAppBackground etc.
currently for me it's like this:
Future<Store<AppState>> createStore({BuildConfig buildConfig}) async {
initLog();
final SharedPreferences _prefs = await SharedPreferences.getInstance();
final Database _database = DatabaseImpl(_buildConfig.apiPoint);
final Recorder _recorder = RecorderImpl.initialize(database: _database);
final Store<AppState> store = Store(
appReducer,
initialState: AppState.initial(buildConfig: _buildConfig, recorder: _recorder),
distinct: true,
middleware: [
MiddlewareTest(),
RecordEntityMiddleware(database: _database),
],
);
**_recorder.store = store;**
return store;
}
not exactly as a global static variable but hacky nonetheless (or is it?)
from flutter_redux.
Thank you for your quick response :)
I'll look through the links you've provided. Your second idea especially seems like it would solve my problem, but then again; passing the store to a widget might break one of the key features of redux (as you've mentioned in your DartConf 2018 talk) which is flexibility in moving classes or UI elements around and keeping things loosely coupled. In the meantime, I'll give you a use-case to further clarify my situation.
Say I have a Widget that displays a button. Pressing it will then trigger a function that calls asks the database for a variable and then assigns a field of my AppState to that variable. I'm using pseudocode for the database part here.
import 'package:flutter/material.dart';
// Temporary variables for demonstration purposes.
dynamic Database;
class AssignUserByDocument {AssignUserByDocument(dynamic document);}
dynamic ExampleSpecialStoreProvider;
class HomeScreen extends StatelessWidget {
void _handleButtonPressed() async {
// Make an async call to the example database.
var document = await Database.getDocument("user").byId("1234");
// This is where I want to dispatch an action to the store
// (but not build a widget, so I can't use StoreConnector, right?).
// Example solution:
ExampleSpecialStoreProvider(
converter: (store) => store,
action: (store) {
store.dispatch(new AssignUserByDocument(document));
}
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: MaterialButton(
onPressed: _handleButtonPressed,
child: Text("Initialize user object from DB!"),
),
));
}
}
Now, it doesn't really matter if the _handleButtonPressed
is async or not for what I'm asking (I've already found a way to do async action dispatching using redux_thunk
), but I want to have a way to access the closest neighbouring store and dispatch an action, but, as you can see, without actually building a widget - a way to access the store without a front-end, so to speak.
I hope this is clarifying things for you.
The solution I'm using now is a simple class with static variables that are accessible from anywhere (I believe that's what a singleton
actually is, correct?) but I'd like to do it through redux.
from flutter_redux.
Gonna go ahead and close this out, please let me know if you need any further help :)
from flutter_redux.
on a similar question, how to I get store instance outside of a widget scope? currently I just save the store instance as a global static variable, however this approach feel very much like a hack. to explain the usage, consider I have a local service like component (one that listen to app lifecycle or network connectivity), which I'd like to have the ability to actively dispatch events such as onAppBackground etc.
currently for me it's like this:Future<Store<AppState>> createStore({BuildConfig buildConfig}) async { initLog(); final SharedPreferences _prefs = await SharedPreferences.getInstance(); final Database _database = DatabaseImpl(_buildConfig.apiPoint); final Recorder _recorder = RecorderImpl.initialize(database: _database); final Store<AppState> store = Store( appReducer, initialState: AppState.initial(buildConfig: _buildConfig, recorder: _recorder), distinct: true, middleware: [ MiddlewareTest(), RecordEntityMiddleware(database: _database), ], ); **_recorder.store = store;** return store; }not exactly as a global static variable but hacky nonetheless (or is it?)
I am having a very similar situation where my app listens to a child in my firebase database.
I attach the listener in the initState of my HomeScreen.
But how do I dispatch an action when I get a response?
class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, VoidCallback>(
converter: // ???
builder: (BuildContext context, VoidCallback callback) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: SomeComponentThatWillDisplayTheResults()
);
},
);
}
_onValueChange(Event event) {
// How do I dispatch an action now ??
}
@override
void initState() {
super.initState();
Firebase.database
.reference()
.child("counter")
.onValue
.listen(_onValueChange);
}
}
from flutter_redux.
My favorite solution.
void _onEditNote() { final store = StoreProvider.of<AppState>(context); store.dispatch(NoteEditAction(note)); }
Yes, I had forgotten that StoreProvider is an InheritedWidget.
This is the correct way to access the store from within a Widget class.
from flutter_redux.
@ramsanath @JuanGamarraDev How do I do this without context?
initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) {
runInitTasks();
});
}
@protected
Future runInitTasks() async {
final store = StoreProvider.of<AppState>(context); // How do I get context??
store.dispatch(connectDatabase);
}
from flutter_redux.
Is there a difference between using
StoreProvider.of<AppState>(context).disparch(action);
on the target widget (for example a button)
and passing to the widget a Function(), build in the ViewModel of the StoreCoverter, that dispatch the action for it?
I mean, is pretty obvious that the simpler solution is to just write the single line of code that take the store from the StoreProvider and dispatch the action but I saw in the flutter Redux Architecture Sample that they prefer to pass as a paramenter a Function that dispatch the action. Is there any difference? using StoreProvider "costs" more that just building a function in the StoreConverter ? maybebecause we already have the store in the scope in the second case?
from flutter_redux.
Related Issues (20)
- How to not accept change notifications when the Router is not at the top level, and then take the initiative to obtain the changes when the Router returns to the top level HOT 1
- onDidChange called twice HOT 8
- Is this package still actively maintained?
- `onWillChange` and `onDidChange` errors are swallowed HOT 4
- Warning related to `?` operator on Linux/Dev channel. HOT 2
- Import Store data type
- Widgets binding error after upgrading flutter version to 2.13.0-0.1.pre HOT 1
- onDidChange not working as expected HOT 3
- Build warning with Flutter 3.0 HOT 5
- flutter_redux depends on flutter_redux, version solving failed HOT 3
- onWillChange and onDidChange called twice after state update HOT 2
- Passing Store as an argument to MethodChannel and Background Isolate
- Question on how to use the reducer right way HOT 2
- [Question]: Any thoughts on difference between ignoreChange and distinct?
- Socket and Flutter_redux HOT 18
- Dispatching an action that does not alter state rerenders the widget, causing an infinite loop HOT 5
- How to watch redux store change inside useEffect? HOT 5
- [Question] is it expected that the store doesn't run until frames render? HOT 1
- After push a new page, and Dispatching an action, there is an infinite loop
- Is flutter redux actively maintained HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from flutter_redux.