Giter VIP home page Giter VIP logo

provider's People

Contributors

arkangel12 avatar bradyt avatar enetor avatar feinstein avatar felangel avatar filiph avatar jaichangpark avatar jmewes avatar joaomarcos96 avatar kaboc avatar kranfix avatar leonardorosaa avatar lsaudon avatar myconsciousness avatar nikitadol avatar nohli avatar passsy avatar rhalff avatar robsonsilv4 avatar rrousselgit avatar selim1238 avatar spkersten avatar srawlins avatar thechinkysight avatar thedevelolper avatar toureholder avatar tp avatar wujek-srujek avatar xiaojieonly avatar xvld avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

provider's Issues

Strange trigger mechanism

I used provider in my project and I fond a strange trigger mechanism.

Widget build(BuildContext context) {
    final notifer = Provider.of<Notifier>(context);
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),

When I push a new page, I noticed that the first page's provider was touched off, but no notifyListeners was called. And when I pop the second page, the first page's provider was touched off again, still no notifyListeners was called. If I removed the
final notifer = Provider.of<Notifier>(context);
then the Widget will not refresh every time I push or pop.

Possibility to have a builder instead of child field on Provider/StatefulProvider

When I have small screen I would like to define them like this:

Widget build(context) {
return StatefulProvider<MyBloc>(
    valueBuilder: (context, old) => old ?? MyBloc(),
    onDispose: (context, bloc) => bloc.dispose(),
    builder: (context, bloc) => StreamBuilder<String>(
        stream: bloc.stream,
        builder: (context, snapshot) => Text(snapshot.data),
        )    
    ),
}

If we want to achieve this currently we need to extract the screen into another widget, and retrieve the bloc on the build method. It can be better IMHO

Cannot provide bloc from inside of other widgets ?

As I was thinking of using Provider + BLoC. Usually with Inherited widget what is possible is to provide a bloc specific to the route / sub child widgets.

When I tried the same with Provider it fails to recognize the Bloc?

But same works in case If I initiate bloc from outside of MaterialApp.


void main() {
  runApp(
    Provider<DevicesBloc>(
      builder: (ctx) => DevicesBloc(),
      child: MaterialApp(
          title: 'WaterLeveller',
          home: DevicesListScreen(),
        ),
      ),    
  );
}

class DevicesListScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Color(0xFF448C33),
        title: const Text("Water Leveler"),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.add),
            onPressed: () {
              Navigator.push(context,
                  MaterialPageRoute(builder: (context) => AddDeviceScreen()));
            },
          )
        ],
      ),
      body: Container(
          child: ListView(
            children: <Widget>[
              GestureDetector(              
                child: Padding(
                  padding: EdgeInsets.all(30),
                  child: Text("List 1"),
                ),
              )
            ],
          ),
        ),    
    );
  }
}

Those are the parent widgets, and the below one is child widget, In which I have a button "Add" on press, I would like to call a function inside the DevicesBloc, which works in this case.

class AddDeviceScreen extends StatelessWidget{
  @override
  Widget build(BuildContext context) {    
    return Scaffold(
      appBar: AppBar(
        title: Text("Add Device"),
      ),
      body: Container(
        margin: EdgeInsets.all(20.0),
        child: Column(
          children: <Widget>[
            TextFormField(
              decoration: InputDecoration(
                hintText: "Device Name"
              ),
              style: TextStyle(
                
              ),
            ),
            Padding(
              padding: const EdgeInsets.fromLTRB(0,14.0,0,0),
              child: RaisedButton(
                onPressed: (){
                  Provider.of<DevicesBloc>(context).addDevice();
                },
                child: Text("Add"),
              ),
            )
          ],
        ),
      ),
    );
  }
}

But same won't work in case I move the bloc provide inside DeviceListScreen like this.


void main() {
  runApp(
   MaterialApp(
          title: 'WaterLeveller',
          home: DevicesListScreen(),
        ),       
  );
}


class DevicesListScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Provider<DevicesBloc>(
      builder: (ctx) => DevicesBloc(),
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: Color(0xFF448C33),
          title: const Text("Water Leveler"),
          actions: <Widget>[
            IconButton(
              icon: Icon(Icons.add),
              onPressed: () {
                Navigator.push(context,
                    MaterialPageRoute(builder: (context) => AddDeviceScreen()));
              },
            )
          ],
        ),
        body: Container(
          child: ListView(
            children: <Widget>[
              GestureDetector(
                child: Padding(
                  padding: EdgeInsets.all(30),
                  child: Text("List 1"),
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

This throws this exception.

I/flutter (18418): ══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
I/flutter (18418): The following ProviderNotFoundError was thrown while handling a gesture:
I/flutter (18418): Error: Could not find the correct Provider<DevicesBloc> above this AddDeviceScreen Widget
I/flutter (18418):
I/flutter (18418): To fix, please:
I/flutter (18418):
I/flutter (18418):   * Ensure the Provider<DevicesBloc> is an ancestor to this AddDeviceScreen Widget
I/flutter (18418):   * Provide types to Provider<DevicesBloc>
I/flutter (18418):   * Provide types to Consumer<DevicesBloc>
I/flutter (18418):   * Provide types to Provider.of<DevicesBloc>()
I/flutter (18418):   * Always use package imports. Ex: `import 'package:my_app/my_code.dart';
I/flutter (18418):   * Ensure the correct `context` is being used.
I/flutter (18418):
I/flutter (18418): If none of these solutions work, please file a bug at:
I/flutter (18418): https://github.com/rrousselGit/provider/issues

Which in my opinion should be possible ? Or is it something should not be done?

[RFC] Should we make a v3.0.0 with name tweaks/constructors swap?

There are a few inconsistencies among the public API

  • StreamProvider.builder from the default constructor requests a StreamController instead of a stream. It'd be more logical for the default constructor to request a Stream, and have a named constructor or another class for the StreamController.

  • XXProvider.value have inconsistencies in the name used for the value. ChangeNotifierProvider requests a notifier, Provider requests a value, ValueListenableProvider requests a valueListenable. But they probably should all be named value.

  • Not an immediate issue, but ProxyProvider.custom has a weird naming issue. It takes both a builder and a providerBuilder argument. Maybe we should globally rename builder to something better (valueBuilder like it originally was?)

These are minor changes but requires a breaking change.

cc @brianegan @filiph

Provide named dependencies

I want to have the ability to provide multiple implementations of one interface (or multiple instances of the same class) and to consume the one I need depending on the name.
Something like this to provide
MultiProvider(
providers: [
Provider.value(value: "Object one", name: "oneone"),
Provider.value(value: "Object two", name: "twotwo"),
],
.......
)

...and to consume
Provider.of(context, name: "oneone")
Provider.of(context, name: "twotwo")

How can achieve this?
Thanks :)

Example

In the example provided on the pub page, I get an error No Directionality widget found. RichText widgets require a Directionality widget ancestor.

I think the example may need to be updated, or some clarification made.

To reproduce I am using VS Code with the latest flutter and dart versions. I cut and pasted the example code into the editor and compiled to get the error.

Doubt with Provider.of

Hello, nice library 😀.
can you explain me please what is the difference.

Provider.of<SomeClass>(context, listen: true)
Provider.of<SoomeClass>(context, listen: false)

I noticed that I can use in initState() of StatefulWidget when is false, and can't use when is true, but really I don't know why :(

Thanks

Provider.of<SomeProvider>(context, listen: false).SomeAPICall()

Good Day Remi,

Could you please explain how to use the provider in initState, i have tried many way and all have failed except doing below

# Future.delayed(Duration.zero, () { Provider.of<PrincipalProvider>(context, listen: false).fetchPrincipals(); });

Is there a better way to implement it?

Also, the code worked perfectly in the scoped_model

ScopedModel.of<PrincipalProvider>(context).fetchPrincipals();

I want to fully embrace the provider model, but I just need some clarity.

Consumer2/3/4/5/6/7

Consumer is cool. But we may want more than one value.
And nesting consumers is not cool.

Let's do consumers with multiple arguments

Consumer2(
  builder: (BuildContext context, int foo, String bar) => Container(),
);

ChangeNotifierProvider example

Is the example code show by @brianegan in "Merge provider and scoped_model"
brianegan/scoped_model#61 the "correct" way to use this provider?

Simple examples of the existing providers would help a lot for users of "scoped_model". Your only example of the 'simple' provider seems to me to be the least useful as it isn't 'reactive' (if I'm reading the docs correctly)

thanks for all your work!

Problem with mixin ChangeNotifier

Hi @rrousselGit ,

Suppose I already have a class A extends B, in order to use the ChangeNotifierProvider, I have to mixin the ChangeNotifier. Now, in my A.dispose, I expect the super.dispose to B.dispose, but it ends up with the ChangeNotifier.dispose. That will somehow ruin my logic.
May I what is the alternative to the ChangeNotifierProvider if the class is not extends/mixin from ChangeNotifier?

Thanks in advance.

Could not find the correct Provider<Bloc> above this ChangeStringWithButton Widget

Hi, this probably isn't a bug, but I don't understand what is going on here.

I get thouse 2 errors:

- Could not find the correct Provider<SecondPageBloc> above this ChangeStringWithButton Widget
- There are multiple heroes that share the same tag within a subtree.

This is my MultiProvider at the Myapp() main page:

home: MultiProvider(
        providers: [
          Provider<CounterBloc>(
            value: CounterBloc(),
          ),
          Provider<UserBloc>(
            value: UserBloc(),
          ),
          Provider<SecondPageBloc>(
            value: SecondPageBloc(),
          ),
        ],
        child: BlocCounterPage(), // the main page
      ),

depending on the child ^ which i am using in the MultiProvider that widget is working but if I navigate to another page with the drawer I get the error mentioned above

The Navigation from the Drawer:

Divider(height: 2.0, color: Colors.blue,),
          ListTile(
              leading: Icon(Icons.text_fields),
              title: Text('Change Text with button'),
              onTap: () {
                Navigator.of(context).pushReplacement(
                    MaterialPageRoute(builder: (BuildContext context) {
                      return ChangeStringWithButton();
                    }));
              }
          ),

This is a simple counter bloc

import 'dart:async';
import 'package:rxdart/rxdart.dart';
import 'package:simple_bloc/api/db_api.dart';
import 'package:simple_bloc/models/user.dart';

class UserBloc {
  User _user;
  final _userController  = BehaviorSubject<User>();
  Stream<User> get outUser => _userController.stream;
  UserBloc(){
    init();
  }
  void init() async {
    _user = await api.getUser();
    _userController.add(_user);
  }
  void updateUser(User user){
    _user = user;
    _userController.add(_user);
  }
}

Another thing If I use pushReplacment to navigate I get a big error:
Could not find the correct Provider<SecondPageBloc> above this ChangeStringWithButton Widget

Provider in multiples routes

Hello if I want to use Provider.of... in multiples routes, the Provider widget must be a parent of material app? Or I can create de Provider widget in any route(no material app widget) and routes pushed by this widget can use Provider.of... ?

Thanks

[RFC] Simplifying providers that depends on each others

From experience looking at larger Flutter applications, it is relatively common to have some dependency between providers.

For example, we may have a Provider<Configuration> and a Provider<AuthentificationService> where the latter depends on the former.

But that's currently not something we can represent purely using provider package. Our only solution right now is to somehow eject provider and handle everything ourselves like so:

Configuration configuration;

Widget build(BuildContext context) {
  return MultiProvider(
    providers: [
      Provider.value(value: configuration),
      Provider(builder: (_) => AuthentificationService(configuration)),
    ],
    child: MaterialApp(...),
  );
}

That's not good.
It reintroduces the ability to make a circular object graph & forget to update/dispose of an object.
We're loosing all the fancy securities that using widgets for DI brings.


The discussion on #44 made me realize that an advanced Consumer may be able to solve this issue.

Naively we could think of doing:

MultiProvider(
  providers: [
    Provider<Foo>(builder: (_) => Foo()),
    Consumer<Foo>(builder: (context, foo, child) {
      final bar = Bar(foo);
      return Provider<Bar>.value(value: bar, child: child);
    }),
  ].
  child: MaterialApp(...),
);

... The problem is that we're doing final bar = Bar(foo); inside the build method of Consumer.
That's not good either – we may have memory leaks.
But that's something we can fix using a more advanced Consumer, which I'll call ProxyProvider for now.

The idea behind ProxyProvider is that it would work similarly to the default constructor of Provider, but also like Consumer as it would read values from other providers and pass them to the builder.

The same example using ProxyProvider would be:

MultiProvider(
  providers: [
    Provider<Foo>(builder: (_) => Foo()),
    ProxyProvider<Foo, Bar>(builder: (context, Foo foo, Bar previousBar) {
      return Bar(foo);
    }),
  ].
  child: MaterialApp(...),
);

In that example, the builder of ProxyProvider would be called on the first build; or whenever Foo changes.

Passing parameters to Bloc constructor

Interesting pattern. Do you have a suggestion for how to use a Bloc that requires a parameter?
I have been using another inherited widget that I use to retrieve the parameter and inject it into Bloc. Is there a convenient way to use this pattern to manage such a case?

Also, the inherited widget apparently does not have a dispose. Does this not result in a possible memory leak if used in stateless widgets (which also does not have a dispose) and in other cases? What is the best way to dispose of Bloc resources?

Here is a project that uses a stateful widget as a Bloc provider that disposes Bloc resources:
https://github.com/boeledi/Streams-Block-Reactive-Programming-in-Flutter/blob/master/lib/blocs/bloc_provider.dart

Here is the article that discusses it:
https://www.didierboelens.com/2018/08/reactive-programming---streams---bloc/

What is the recommended method to manage memory, or am I missing something?

Example for providers

It will be great if we can have an example of every available provider to start with. Its easier for a guy like me who is just starting with flutter and provider to understand it better.

Infinite Listview with provider

I'm making fintech app with flutter and i was managed login workflow with this library but i have three listview (datas came from API , im not sure how big it is ) such as ; Income,Outcome and AllOfThem.

I know ListView.Builder and scrollController but sometimes i need to add some items to list and call the API for adding items also change the UI screen.

Can i manage this , using your library ?

If answer is yes , How ? If answer is no , Why ?

Shouldn't this rebuild?

class TestPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) => HookProvider(
        hook: () => useState(0),
        child: Consumer<ValueNotifier<int>>(
          builder: (context, index) => Scaffold(
                appBar: AppBar(
                  actions: <Widget>[
                    FlatButton(
                      child: Text("Second screen"),
                      onPressed: () => showDialog(
                          context: context,
                          builder: (context) => HookProvider(
                                hook: () => index,
                                child: Consumer<ValueNotifier<int>>(
                                  builder: (context, index) => Scaffold(
                                        appBar: AppBar(
                                          title: Text("Second screen"),
                                        ),
                                        body: Center(
                                          child: FlatButton(
                                            child:
                                                Text("Index: ${index.value}"),
                                            onPressed: () => index.value++,
                                          ),
                                        ),
                                      ),
                                ),
                                updateShouldNotify: (_, __) => true,
                              )),
                    )
                  ],
                ),
                body: Center(
                  child: FlatButton(
                    child: Text("Index: ${index.value}"),
                    onPressed: () => index.value++,
                  ),
                ),
              ),
        ),
        updateShouldNotify: (_, __) => true,
      );
}

What i'm trying to do is change a hook's value from within another screen, and it works... kinda, the value is updated but the second screen's widget is not rebuilt for some reason (if you go back you can see the new value). So is it a bug or this behavior should be expected?

MultiProvider have one Provider listen to another Provider?

What would be the best pattern for breaking a large Provider into two Providers, but still have one of them listen to changes on the other. For example I want to have two Providers, one named User and one named Favorites. If the User changes then the Favorites needs to reload and notify its own listeners.

Multiprovider could not find the correct provider above this widget

I think this is a bug

https://stackoverflow.com/questions/56214296/cound-not-find-the-correct-provider-above-this-consumer-widget

It should working but it say could not find the correct Provider
I try to change to this too still not work

class _LoginViewState extends State<LoginView> {
  final TextEditingController _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget> [
        StreamBuilder<Chat>(
          stream: Provider.of<Socketio>(context).streamChat,
          builder: (context, snapshot) {
            return Text(snapshot.data?.toString() ?? 'Foo');
          },
        ),
        BaseView<LoginModel>(
          builder: (context, model, child) => Scaffold(
            backgroundColor: backgroundColor,
            body: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                LoginHeader(
                  validationMessage: model.errorMessage,
                  controller: _controller),
                model.state == ViewState.Busy
                ? CircularProgressIndicator()
                : FlatButton(
                  color: Colors.white,
                  child: Text(
                    'Login',
                    style: TextStyle(color: Colors.black),
                  ),
                  onPressed: () async {
                    var loginSuccess = await model.login(_controller.text);
                    // await model.login(_controller.text);
                    if(loginSuccess){
                      Navigator.pushNamed(context, '/');
                    }
                  },
                )
            ],),
          ),
        )
      ]
    );
  }

this is where Im declare the provider in main.dart

void main() {
  setupLocator();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return  MultiProvider(
      providers: [
        StreamProvider<User>(
          initialData: User.initial(),
          builder: (context) => locator<AuthenticationService>().userController,
        ),
        StreamProvider<Chat>(
          initialData: Chat.initial(),
          builder: (context) => locator<Socketio>().chatController,
        ),
      ],
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(),
        initialRoute: '/login',
        onGenerateRoute: Router.generateRoute,
      ),
    );
  }
}

HookProvider

Hooks allow some very powerful provider:

HookProvider<MyBloc>(
  value: () {
    final bloc = useMemoized(() => MyBloc());
    useEffect(() => bloc.dipose, [bloc]);
    return bloc;
  },
  child: Container();
);

You likely want a child argument on the Consumer constructor too

See https://docs.flutter.io/flutter/widgets/AnimatedBuilder/AnimatedBuilder.html

Otherwise you can end up in performance inefficiencies where a top-part of a subtree depends on the thing dependency injected but the rest of the tree doesn't. You'd end up constantly rebuilding that whole subtree.

Letting Consumer have a child instance and letting its builder have BuildContext context, Widget child, T value can let the builder stop the tree rebuild traversal by putting the same child instance back in the returned widget subtree.

How to do some init actions ?

such as: in my app, i need to request some configs from server(maybe several apis), but this function(which combined these apis, but these child functions are in different providers/states) should be called exactly only once. so what's the correct way to do with it? because of build and didChangeDependencies function may called multi times, so i can't call init in these function, but in initState, the 'listen' parameter can only be 'false' and this will throw exception if i call multi different child function.

Examle:

i have A,B states

in A:
class AStates with ChangeNotifier {
...
appInit() async{
...
childInitFunc1();
childInitFunc2();
childInitFunc3();
// if i call initB it will throw exception whatever listen is true or false
Provider.of(context,listen:false).initB();
}
...
}

in B:
class BStates with ChangeNotifier {
...
initB() async{
...
}
...
}

in index.dart:
class Test extends StatefulWidget {
@OverRide
_TestState createState() => _TestState();
}

class _TestState extends State with WidgetsBindingObserver {
@OverRide
void initState() {
//
// init here and if listen is true, it will throw exception
//
Provider.of(context, listen:false).appInit(state);
WidgetsBinding.instance.addObserver(this);
super.initState();
}

@OverRide
void didChangeDependencies() {
// can't init here
super.didChangeDependencies();
}

@OverRide
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}

@OverRide
void didChangeAppLifecycleState(AppLifecycleState state) {
Provider.of(context).changeAppLifecycleState(state);
super.didChangeAppLifecycleState(state);
}

@OverRide
Widget build(BuildContext context) {
// can't init here
return Container();
}
}

so what's wrong with my code? and what should i do correctly ?

Benefits & dangers of re-using values in StatefulProvider

First of all, your StatefulProvider is a really neat method to deal with disposing blocs that live in inherited widgets. I'm certainly going to use this pattern! In the following I'm assuming it's used specifically to serve blocs.

I want to share a concern: The implementation allows to reuse a previous value by passing it to valueBuilder. I'm not sure if this actually does what I guess it's supposed to do, and worse, if it might even impose the risk of unwanted/dangerous side effects.

If valueBuilder was expected to be called multiple times on the same widget (the provider), and creation of the value is expensive, then I would see a clear benefit in re-using an old value. But does that actually happen? valueBuilder is called during initState, in which case previous will obviously be null for a new provider instance and a new state. It's also called during didUpdateWidget, but that should happen only if the provider widget itself changed. So according to my understanding, the original provider widget will never actually make use of this reuse mechanism. But then what's the point? Maybe to share a value between multiple different Providers? That would seem very fragile to me, given Flutter's particular update handling of Widgets, Elements, and States.

Example

Say there's Provider<Bloc> providerA that generates a _StatefulProviderState<Bloc> with _value = blocA. Now, under very specific circumstances providerA gets replaced by a new Provider<Bloc> providerB (same type, same key). Both providerA and providerB have the same valueProvider: (_, bloc) => bloc ?? Bloc(). Since Flutter sees the chance to re-use the already existing _StatefulProviderState<Bloc>, it wires it to providerB. Now, providerB works with blocA instead of a fresh instance of Bloc. As the package user, I would not expect this behavior.

Clearly I might be overlooking an important detail or my assumptions could be wrong – could you please explain why you think this specific implementation of re-using an old value is beneficial, and if my concern regarding side effects is unwarranted?

can't use dart 2.3.0 with provider

my pubspec.yaml has
sdk: ">=2.1.0 <3.0.0"

If I use 2.3.0 features I get

The for, if and spread elements were not supported until version 2.2.2, but this code is required to be able to run on earlier versions.
Try updating the SDK constraints.

if I change the pubspec.yaml to 2.3.0 as the lower bound then flutter get spits back
Running "flutter packages get" in flutter_provider...
The current Dart SDK version is 2.3.0-dev.0.5.flutter-a1668566e5.

Because flutter_provider requires SDK version >=2.3.0 <3.0.0, version solving failed.
pub get failed (1)
exit code 1

here is my flutter --version
Flutter 1.5.4-hotfix.2 • channel beta • https://github.com/flutter/flutter.git
Framework • revision 7a4c33425d (2 weeks ago) • 2019-04-29 11:05:24 -0700
Engine • revision 52c7a1e849
Tools • Dart 2.3.0 (build 2.3.0-dev.0.5 a1668566e5)

Consider adding back StatefulProvider

Consider adding back StatefulProvider

Version 2.0.0 merges StatefulProvider into Provider and leads to a nicer API.

However, with this change some use cases are no longer supported.

For example, I have an email & password sign in form where the user can switch between 3 different modes: sign in, register, reset password:

example

The logic for updating the state is handled inside a bloc, and I use a StreamBuilder to update the UI.

When the system keyboard appears, the entire page is rebuilt.

And with StatefulProvider everything works fine, because the bloc is not created again when the keyboard appears (this is expected, as a StatefulWidget's state is preserved when the parent is rebuilt).

However, this doesn't work with provider: 2.0.0, because when the keyboard appears the entire bloc is rebuilt and the UI is rebuilt with the initial state rather than the previous state.

This leads to some unexpected behaviour where the form state changes when I start typing on a text field:

email-password-provider

This is just an example, but I have a more complex app with a lot of nested StatefulProviders. And I can see how rebuilds in parent widgets would cause a lot of problems, as my blocs would be re-created and their state reset.

Ultimately the problem is that we need a Provider that inherits from StatefulWidget rather than InheritedWidget, so that the value/bloc is created in initState() and the state is preserved when a parent widget is rebuilt.

MultiProvider and two or more ChangeNotificationProviders

The app works like this:
It has a Counter with ChangeNotifier and a ThemeSwitch also with ChangeNotifier
if number > 5 it changes to dark.
Consumer2 is used ( the logic for switching is made inside Consumer2).

I have this question too: How can I have access to Counter value inside the ThemeSwitch class to make the logic there.

The error happens when one of them is rebuilding the same descendants when the other is rebuilding at the same time.

Error:
Exception has occurred. FlutterError (setState() or markNeedsBuild() called during build. This ChangeNotifierProvider<ThemeSwitch> widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase. The widget on which setState() or markNeedsBuild() was called was: ChangeNotifierProvider<ThemeSwitch>(state: _ListenableProviderState<ThemeSwitch>#e36d8) The widget which was currently being built when the offending call was made was: Consumer2<ThemeSwitch, Counter>(dirty, dependencies: [_Provider<ThemeSwitch>, _Provider<Counter>]))

Files:

// main.dart
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
import 'package:provider_tutorial/counter.dart';
import 'package:provider_tutorial/theme_switch.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return
        // inject providers
        MultiProvider(
      providers: [
        ChangeNotifierProvider<Counter>(builder: (context) => Counter(0)),
        ChangeNotifierProvider<ThemeSwitch>(
          builder: (context) => ThemeSwitch(ThemeState.light),
        ),
      ],
      // 2 Consumers
      child: Consumer2<ThemeSwitch, Counter>(
        builder: (context, themeSwitch, counter, widget) {
          themeSwitch.themeState =
              (counter.value > 5) ? ThemeState.dark : ThemeState.light;
          return MaterialApp(
            title: 'Flutter Demo',
            theme: themeSwitch.theme,
            home: MyHomePage(),
          );
        },
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final counterProvider = Provider.of<Counter>(context);
    print(counterProvider.value.toString());

    return Scaffold(
      appBar: AppBar(
        title: Text('Providers Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'If Number > 5 theme is dark:',
            ),
            Consumer<Counter>(
              builder: (context, counter, widget) {
                return Text(
                  '${counter.value}',
                  style: Theme.of(context).textTheme.display1,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          FloatingActionButton(
            onPressed: () {
              counterProvider.increment();
            },
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
          SizedBox(
            height: 20,
          ),
          FloatingActionButton(
            onPressed: () {
              counterProvider.decrement();
            },
            tooltip: 'Decrement',
            backgroundColor: Colors.red,
            child: Icon(Icons.remove),
          ),
        ],
      ),
    );
  }
}

// theme_switch.dart
import 'package:flutter/material.dart';

enum ThemeState { light, dark }

class ThemeSwitch with ChangeNotifier {
  ThemeSwitch(this._themeState);

  ThemeState _themeState;
  ThemeData _theme;

  ThemeState get themeState => _themeState;
  ThemeData get theme => _theme;

  set themeState(ThemeState themeState) {
    _themeState = themeState;

    if (_themeState == ThemeState.light) {
      _theme = ThemeData.light();
    } else {
      _theme = ThemeData.dark();
    }
    notifyListeners();
    print("theme state changed");
  }
}
// counter.dart
import 'package:flutter/foundation.dart';

class Counter with ChangeNotifier {
  Counter(this._value);
  int _value = 0;

  int get value => _value;

  set value(int value) {
    _value = value;
    notifyListeners();
  }

  increment() {
    value++;
  }

  decrement() {
    value--;
  }
}

Is ListenableProvider needed?

@jiaeri said:

Do you think ListenableProvider is needed? It does add some complexity, where it might be cleaner on the consumer side

A ListenableConsumer can just use an ordinary provider, and look like this:

class ListenableConsumer extends StatelessWidget {
final Widget Function(BuildContext context, T value, Widget child) builder;

ListenableConsumer({Key key, @required this.builder}) : super(key: key);

@override
Widget build(BuildContext context) {
return Consumer(
builder: (context, value) =>
AnimatedBuilder(animation: value, builder: builder));
}
}

How to perform action later after network result?

I'm not sure provider lib is able to do this, but let see my case:

  • Assume in Login screen, I touch on Login button to log in.
  • After logging successfully, I would like to open Home screen. But I see in Login screen, there is only build() method to update its UI but no place to handle Login success action.

Could you please share with me your experience in similar case? Thanks.

StreamProvider

A shorthand to wrapping a HookProvider into a StreamBuilder or equivalent.

Rename SingleChildCloneableWidget

I really like this package. Especially the straightforward naming (coming from scoped_model)

Would it be possible to rename SingleChildCloneableWidget to something like BaseProvider or AbstractProvider to make it more readable?

I suppose most users don't ever use this class but my app is quite complex and I find it very useful...

ChangeNotifier performance

ChangeNotifier is O(N^2) for notify listeners. It has to make a copy of the list of listeners, iterate that copy, and during the iteration do another iteration of the original list to see if the listener is still registered.

This seems particularly troubling for the MultiProvider case (which would get to O(N^3)), but could be a problem anywhere you have lots of widgets listening to the same provider.

A possible optimization here is to see if we can stuff the callbacks into a set or map, e.g. by having addListener and removeListener (or the equivalent wrappers of them) using integer handles instead of the actual callback - or just using a set if that works for callbacks (I'm not sure how hashCode is handled on a var that is a closure), which should get this back to O(N).

Invalid coverage report

For unknown reasons, codecov.io says that /lib/src/adaptive_widget_builder.dart is only partially covered by tests when it has in fact 100% coverage.

Should we add `dispose` to ChangeNotifierProvider

I know ChangeNotifierProvider automatically disposes of the provided value itself.

But I hit a case when I'm creating a Timer that modifies the ChangeNotifier during its lifetime. (That's obviously just a sample, and it's too synthetic and generally bad.) In this case, it would be nice to be able to dispose of the Timer with the ChangeNotifier.

I hypothesize that something similar will quite often happen in the real world as well. For example, you have a ChangeNotifier that you use as a "view model", and network events can change it. Now, you could put the network logic into the ChangeNotifier, but sometimes it's nicer to have it as a separate class. For example, you could have:

ChangeNotifierProvider(
  builder: (context) {
    var network = MyNetworkConnection();
    return MyModel(network);
  }
  dispose: ???,
  child: ...
}

Welp, I just realized that you can't do that, since the dispose callback wouldn't give you the MyNetworkConnection object.

So, never mind. This is probably another use case for ProxyProvider, where I provide the MyNetworkConnection to the ChangeNotifierProvider<MyModel>.

Feel free to close this immediately. Just wanted to have this around for future reference.

Flutter Web support

Can we get repackeged version of provider library for Flutter Web?

Flutter Web is using widgets from package:flutter_web instead of package:flutter.

Lazy load providers

One thing I don't get it:
An app with master and detail views.
I want to supply a DAO class for each view.
Should I use a Multiprovider right after main() or should I use individual Providers in the views class?

I want to instatiate the classes only when the view is loaded. My question is do they get instantiated already if I put it on the top of the app or do they have like lazy instantiation

Originally posted by @peekpt in #77 (comment)

Pass provider via navigator?

Hello,

I am learning provider way & found difficulty in lifting state up. I have three screens,

  • Item listing screen
  • Create item screen
  • Edit item screen

I lift state up in item's listing screen & use provider there. Which is working perfectly fine. But now I need to access that same provider in create & edit screen.

One thing I can do is, initiate provider in MaterialApp() declaration, but problem is I have another 4-5 providers for other parts of the application, & it's not feeling good to put all providers in MaterialApp().

So, what I have done is, I am passing itemProvider of listing screen to create & edit screen in Navigator as argument like this.

Navigator.pushNamed( context, "item.create", arguments: { 'itemProvider': Provider.of<ItemProvider>(context) }, )

My question is, is it a good thing to do or is there any other way to do this?

How to check if ChangeNotifier has been disposed

I'm just trying out Provider for the first time, and struggling to find the equivalent of State's mounted property.

I have a class that mixes in ChangeNotifier. In one of its methods it awaits on a network call, then notifyListeners() with the result of that network call.

If the user has navigated away while the network call is pending, I get an error

E/flutter (27530): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: A XXX was used after being disposed.
E/flutter (27530): Once you have called dispose() on a XXX, it can no longer be used.
E/flutter (27530): #0      XXX&Object&ChangeNotifier._debugAssertNotDisposed.<anonymous closure> (package:flutter/src/foundation/change_notifier.dart:105:9)
E/flutter (27530): #1      XXX&Object&ChangeNotifier._debugAssertNotDisposed (package:flutter/src/foundation/change_notifier.dart:111:6)
E/flutter (27530): #2      XXX&Object&ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:200:12)

In the basic StatefulWidget scenario, I would just check if (mounted) before calling setState. Is there an equivalent for ChangeNotifier?

Thanks!

Status not changing when user authenticating.

Hi , i have some issue or some miss understanding in here. I'm trying to authentication flow.When i click the login button without delaye status is not changing but if i add future delaye in signIn method status flow like this ;

UnAuthenticated => Authenticating => Authenticated

without future delayed ;

UnAuthenticated ==> Authenticated

PS:I have dummy repository for authentication and it has future delayed itself.

here is my sample of project ;

https://file.io/QxnV8h

Thank you ,

[REQ] Documented diagrams

I did this simple sketch example, I'm no expert, but I think if the dev team adds diagrams to documentation will help a lot people that are starting to learn this Plugin.
There's information missing like how the values behave, how they update, etc...
With all this providers and ways to reach the provided values it is start to get confusing.

Provider

Filtering Consumer updates

Having a way to filter updates at the consumer level may be useful to fine-grain builds.

For example, if we have a ChangeNotifier model with multiples properties,notifyListeners would be called each time a value of a property changed. If we have a Consumer for that model that only depends on one of the properties, it is updated even when an other property changes.

We could have a bool shouldBuild(T previousValue, T newValue) argument for Consumer for that purpose.

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.