首页 > 解决方案 > MultiProvider 和抽象类

问题描述

再会!

如何使用 MaterialApp 的提供者?我有一个 MultiProvider 和抽象类。需要将身份验证传递给 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(),
             },
          ),

我有一个抽象类 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 {
   
  }
}

错误是:

错误:在此 StreamBuilder 小部件上方找不到正确的提供程序

这可能是因为您使用了BuildContext不包括您选择的提供者的 a。有几种常见的场景:

  • 您尝试读取的提供程序位于不同的路径中。

    提供者是“范围的”。因此,如果您在路由中插入提供程序,那么其他路由将无法访问该提供程序。

  • 您使用的BuildContext是您尝试读取的提供程序的祖先。

    确保 StreamBuilder 在您的 MultiProvider/Provider 下。这通常发生在您创建提供程序并尝试立即读取它时。

    例如,而不是:

      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),
      ),
    );
  }

标签: flutterdartproviderbloc

解决方案


在转到问题的抽象主题之前,我建议在主文件夹上启动 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(),
  ));
}

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


推荐阅读