Giter Site home page Giter Site logo

Comments (12)

brianegan avatar brianegan commented on July 19, 2024 17

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:

  1. Use the StoreConverter to create a ViewModel that contains functions that dispatch actions to the store for ya. For example, you can create a StoreConnector with a converter that returns a Function(). 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
  2. 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 the store within the methods of that class or it's State class.
  3. 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.

JuanGamarraDev avatar JuanGamarraDev commented on July 19, 2024 14

My favorite solution.

void _onEditNote() { final store = StoreProvider.of<AppState>(context); store.dispatch(NoteEditAction(note)); }

from flutter_redux.

brianegan avatar brianegan commented on July 19, 2024 5

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 forLoadUserActions 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.

zaun avatar zaun commented on July 19, 2024 4

Nevermind. It's global in stateful widgets. I'm an idiot today I guess.

from flutter_redux.

brianegan avatar brianegan commented on July 19, 2024 1

@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.

hereisderek avatar hereisderek commented on July 19, 2024 1

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.

jeroen-meijer avatar jeroen-meijer commented on July 19, 2024

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.

brianegan avatar brianegan commented on July 19, 2024

Gonna go ahead and close this out, please let me know if you need any further help :)

from flutter_redux.

rsanath avatar rsanath commented on July 19, 2024

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.

rsanath avatar rsanath commented on July 19, 2024

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.

zaun avatar zaun commented on July 19, 2024

@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.

VenturaLorenzo avatar VenturaLorenzo commented on July 19, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.