MultiProvider 和摘要 class

MultiProvider and abstract class

美好的一天!

如何使用 MaterialApp 的 Provider?我有一个 MultiProvider 和抽象 class。需要将 auth 传递给 LandingPage

这是我想获得的:

 Widget build(BuildContext context) {
    return Provider<AuthBase>(
      create: (context) => Auth(),
      child: MaterialApp(
        title: "Bloc Test",
        theme: ThemeData(primarySwatch: Colors.indigo),
        home: LandingPage(),
      ),
    );
  }

这是我的工作代码:

 class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MultiProvider(
          providers: [
            ChangeNotifierProvider<ToDoProvider>(
              create: (ctx) => ToDoProvider(),
            ),
          ],
          child: MaterialApp(
            debugShowCheckedModeBanner: false,
            title: 'Ultimative ToDo',
            theme: ThemeData(
              scaffoldBackgroundColor: myListMainColor,
            textTheme:
                  GoogleFonts.sourceSansProTextTheme(Theme.of(context).textTheme),
              visualDensity: VisualDensity.adaptivePlatformDensity,
             
            ),
            initialRoute: '/',
            routes: {
              '/': (context) => LandingPage(auth: Auth()),
              OpenedToDo.routeName: (context) => OpenedToDo(),
             },
          ),

我有一个抽象的 class AuthBase,但我不能将它与 ChangeNotifier 混合使用。所以这就是为什么我不能将新的代码字符串放入 MultiProvider 的原因。

abstract class AuthBase {
  User get currentUser;
  Future<User> signInAnonymously();
  Stream<User> authStateChanges();
  Future<void> singOut(BuildContext context);
  Future<User> singInWithGoogle();
  Future<User> createUserWithEmailAndPassword(String email, String password);
  Future<User> signInWithEmailAndPassword(String email, String password);
}

class Auth implements AuthBase {
  final _firebaseAuth = FirebaseAuth.instance;

  @override
  Stream<User> authStateChanges() => _firebaseAuth.authStateChanges();

  @override
  User get currentUser => _firebaseAuth.currentUser;

  @override
  Future<User> signInAnonymously() async {
  
  }

  @override
  Future<User> signInWithEmailAndPassword(String email, String password) async {
   
  }

  @override
  Future<User> createUserWithEmailAndPassword(
   
  }

  @override
  Future<User> singInWithGoogle() async {
  
  }

  @override
  Future<void> singOut(BuildContext context) async {
   
  }
}

错误是:

Error: Could not find the correct Provider above this StreamBuilder Widget

This likely happens because you used a BuildContext that does not include the provider of your choice. There are a few common scenarios:

  • The provider you are trying to read is in a different route.

    Providers are "scoped". So if you insert of provider inside a route, then other routes will not be able to access that provider.

  • You used a BuildContext that is an ancestor of the provider you are trying to read.

    Make sure that StreamBuilder is under your MultiProvider/Provider. This usually happen when you are creating a provider and trying to read it immediately.

    For example, instead of:

      return Provider<Example>(
        create: (_) => Example(),
        // Will throw a ProviderNotFoundError, because `context` is associated
        // to the widget that is the parent of `Provider<Example>`
        child: Text(context.watch<Example>()),
      ),   }   ```
    
    consider using `builder` like so:
    
    ```   Widget build(BuildContext context) {
      return Provider<Example>(
        create: (_) => Example(),
        // we use `builder` to obtain a new `BuildContext` that has access to the provider
        builder: (context) {
          // No longer throws
          return Text(context.watch<Example>()),
        }
      ),   }   ```
    

着陆页:

 @override
  Widget build(BuildContext context) {
    return StreamBuilder(
      stream: auth.authStateChanges(),
      builder: (context, snapshot) {
        //* если подключился к данным
        if (snapshot.connectionState == ConnectionState.active) {
          //* получаем данные о пользователе
          final User user = snapshot.data;

          print('~ uid is ${user?.uid}');

          if (user == null) {
            //Navigator.of(context).pushNamed(SignInPage.routeName, arguments: auth);

            return SignInPage.create(context);
          } else {
            // Navigator.of(context).pushNamed(HomeScreen.routeName);
            return HomeScreen();
          }
        }
        return Scaffold(
          body: Center(
            child: CircularProgressIndicator(),
          ),
        );
      },
    );
  }

SignInPage 有这部分代码:

 static Widget create(BuildContext context) {
    final auth = Provider.of<AuthBase>(context, listen: false);
    return Provider<SignInBloc>(
      create: (_) => SignInBloc(auth: auth),
      //* обязательно должен быть dispose
      dispose: (_, bloc) => bloc.dispose(),
      //* consumer помогает прокинуть данные в конструктор
      child: Consumer<SignInBloc>(
        child: SignInPage(),
        builder: (_, bloc, __) => SignInPage(bloc: bloc),
      ),
    );
  }

在转到问题的抽象主题之前,我建议在主文件夹上启动 MultiProvider。 像这样

import 'package:provider/provider.dart' as provider;

void main() {
  runApp(provider.MultiProvider(
    providers: [
      provider.ChangeNotifierProvider.value(value: Prov1()),
      provider.ChangeNotifierProvider.value(value: Prov2()),
      provider.ChangeNotifierProvider.value(value: Prov3()),
      provider.ChangeNotifierProvider.value(value: Prov4()),
    ],
    child: MyApp(),
  ));
}

从现在开始,您可以在您的应用程序的任何位置进行访问。 也许在实现抽象 class 的 classes 上使用您想要的提供程序,而不是直接在其上更改数据。希望我能帮上忙,把事情弄清楚。

好的,我找到了解决方法。这很容易。 因此,在 multiProvider 的代码部分的 main.dart 中,我应该只添加一个字符串:

 class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MultiProvider(
          providers: [
        ChangeNotifierProvider<ToDoProvider>(
          create: (ctx) => ToDoProvider(),
        ),
        THIS CODE ==> Provider<AuthBase>.value(value: Auth()),
      ],
      child: MaterialApp(
...

LandingPage.dart:

class LandingPage extends StatelessWidget {
  static const routeName = '/landingPage';

  @override
  Widget build(BuildContext context) {
    final auth = Provider.of<AuthBase>(context, listen: false);
    return StreamBuilder(
      stream: auth.authStateChanges(),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.active) {
          final User user = snapshot.data;
          print('~ uid is ${user?.uid}');
            if (user == null) {
            return SignInPage.create(context);
          } else {
            // Navigator.of(context).pushNamed(HomeScreen.routeName);
            return HomeScreen();
          }
        }
        return Scaffold(
          body: Center(
            child: CircularProgressIndicator(),
          ),
        );
      },
    );
  }
}

在SignInPage.dart中:

static Widget create(BuildContext context) {
    final auth = Provider.of<AuthBase>(context, listen: false);
    return Provider<SignInBloc>(
      create: (_) => SignInBloc(auth: auth),
      dispose: (_, bloc) => bloc.dispose(),
      child: Consumer<SignInBloc>(
        child: SignInPage(),
        builder: (_, bloc, __) => SignInPage(bloc: bloc),
      ),
    );
  }

所以因为 AuthBase 是抽象的 class 我不能将它与 ChangeNotifier 混合,但我不需要那样,因为 class 是不可变的。这就是为什么我使用 Provider.value(value: value).