Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy load providers #81

Closed
rrousselGit opened this issue May 21, 2019 · 19 comments · Fixed by #243
Closed

Lazy load providers #81

rrousselGit opened this issue May 21, 2019 · 19 comments · Fixed by #243
Labels
enhancement New feature or request
Milestone

Comments

@rrousselGit
Copy link
Owner

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)

@rrousselGit rrousselGit added the documentation An opportunity for improving the documentation label May 21, 2019
@rrousselGit
Copy link
Owner Author

Providers are not lazy-loaded, so the topic of "should I put a provider above MyApp or inside the view" is a valid question.

The general answer is "it depends".
We need some examples showing how to lazy-load information using provider.

@rrousselGit
Copy link
Owner Author

After some dig-in in Flutter sources, and once a PR land, provider may be able to do more than expected.

I made a POC of a lazy loading InheritedWidget here https://github.com/rrousselGit/lazy_provider

@rrousselGit
Copy link
Owner Author

After some more playing around, I made an initial implementation of lazy-loaded providers on the branch lazy: https://github.com/rrousselGit/provider/tree/lazy

This currently adds a constructor to Provider:

Provider.lazy(
  builder: (context) => Foo(),
  child: <whatever>
);

where builder is called the first time Provider.of<Foo> is called, instead of the first time Provider<Foo> is built.

That logic could be applied to all providers.

On the other hand, a maintainState-like variable can only be made after a PR on Flutter.

@peekpt
Copy link

peekpt commented Jun 15, 2019

Just a reminder how to test branches

provider:
    git:
      url: https://github.com/rrousselGit/provider.git
      ref: lazy
      path: packages/provider

@peekpt
Copy link

peekpt commented Jun 15, 2019

I can't get this to work.

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

void main() => runApp(Provider<Foo>.lazy(
      builder: (_) {
        Foo('bar');
        print('Foo is provided');
      },
      child: MaterialApp(
          title: 'Lazy Provider',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: Home()),
    ));

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('Home Page build');

    onPressed() {
      Navigator.of(context)
          .push(MaterialPageRoute(builder: (context) => NextPage()));
    }

    return Scaffold(
      appBar: AppBar(title: Text('Lazy Provider')),
      body: Center(
          child: RaisedButton(
        onPressed: onPressed,
        child: Text('Go to Next Page'),
      )),
    );
  }
}

class NextPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('Next Page build');
    return Scaffold(
      body: Center(
        child: Consumer<Foo>(
          builder: (ctx, foo, widget) {
            print('Consuming...');
            return Text(foo?.name ?? 'error');
          },
        ),
      ),
    );
  }
}

class Foo {
  final String name;

  Foo(this.name) {
    print('Foo inited with $name');
  }
}
Restarted application in 1 377ms.
I/flutter (16414): Home Page build
I/flutter (16414): Next Page build
I/flutter (16414): Foo inited with bar
I/flutter (16414): Foo is provided
I/flutter (16414): Consuming...

@rrousselGit
Copy link
Owner Author

What do you mean by that? From the logs it looks like it worked

@peekpt
Copy link

peekpt commented Jun 15, 2019 via email

@rrousselGit
Copy link
Owner Author

That's because your provider's builder doesn't return anything.

@peekpt
Copy link

peekpt commented Jun 15, 2019

yes, that's it! It worked. Thank you! it's weird how dart analyzer doesn't complain about it.

One question please: when Provider is lazy loaded the widget level of the Provider remains on top, or starts where it was instantiated?

@rrousselGit
Copy link
Owner Author

In your example, the widget is still technically on the top of everything.
The only difference is that builder is not called immediately.

@diegotori
Copy link

@rrousselGit Any word on when this new feature will be ready, since this will make it easier for me to inject lazily created scoped dependencies to a given widget. Thanks in advance.

@rrousselGit
Copy link
Owner Author

It should be available within a 2 weeks span.
I waited for flutter/flutter#33213 for a second feature that works in combination with lazy-loading.
But the Flutter team isn't convinced, so I'll start lazy-loading as a standalone feature.

@diegotori
Copy link

@rrousselGit Sorry for pestering you again, just wondering the current status of this feature. I noticed that it's currently Pull Requested, but no movement has been made on it for over a month. Thanks in advance.

@rrousselGit
Copy link
Owner Author

Ah right, sorry for that. I got absorbed by other things.

The basics are fine, and I can make a preview release. But there are still some things I'm not too sure about.

After all, it's technically a breaking change (almost it's not for most use cases), which implies a 4.0.0.
But i have a few other things in mind for a 4.0.0.

Is a 4.0.0-dev alright for now?

I can also make an issue with all the things I have in mind for such version, if you want to comment on it.

@diegotori
Copy link

A 4.0.0-dev preview might suffice for the time being. It's your call though.

Alternatively, if the basics are fine, this could warrant an MVP minor release. Then again, the breaking change factor definitely makes it a major change.

It's a tough call but it's yours to make ultimately. What's the scope of the breaking changes to be exact? That would help us determine whether a minor release for a basic lazy implementation is warranted. Then you can follow that up with more lazy functionality in the next major release at your own pace.

@rrousselGit
Copy link
Owner Author

XXProvider(builder: (_) => Whatever()) would go from non-lazy-loaded to lazy-loaded.

That's a breaking change, because someone may want to voluntarily pre-load the object (to make an http-requests earlier for example).

@diegotori
Copy link

@rrousselGit Is there a possibility that you can make lazy provider functionality either as an optional parameter (i.e. lazyBuilder) which if present, uses that builder instead of the default one, or as a factory method that instantiates it in a different way, hiding the use of this lazy builder?

That way, you can mitigate any potential API breakage by making it opt-in instead of permanent, which is what you're proposing. In a future major release, you can most certainly make this current behavior permanent. However, for now, it might make sense to isolate it from the main constructor to provide for a smooth transition.

@rrousselGit
Copy link
Owner Author

I don't like the idea of making a stable release, knowing that the API is not ideal and will need a breaking change.

And there are still things that need to be decided, such as: how to lazy load an object, but scope it to a Route?
Because currently, scoping to a route relies on the .value variant of providers, which means no lazy-loading.

@rrousselGit
Copy link
Owner Author

Alright sorry for the delay.
I'm working full time on it now and got a good chunk of it down in #240

Everything but Stream/FutureProvider/ValueListenableProvider are migrated, including the *Proxy variant.

The next step is a lot of tests, and merging *Provider and *ProxyProvider (deprecating *Proxy).

@rrousselGit rrousselGit mentioned this issue Nov 26, 2019
@rrousselGit rrousselGit added this to the 4.0.0 milestone Nov 30, 2019
@rrousselGit rrousselGit changed the title Are providers lazy loaded? Lazy load providers Nov 30, 2019
@rrousselGit rrousselGit added enhancement New feature or request and removed documentation An opportunity for improving the documentation labels Nov 30, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants