You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中MultiProvider结合抽象类实现多登录提供商区分调用问题

问题

我尝试为Google和Facebook登录创建统一接口,SignInProvider定义如下:

abstract class SignInProvider with ChangeNotifier {
  bool get isSigningIn;
  set isSigningIn(bool isSigningIn);
  void login();
  void logout();
}

class FacebookSignInProvider with ChangeNotifier implements SignInProvider { ... }
class GoogleSignInProvider with ChangeNotifier implements SignInProvider { ... }

首页代码如下:

class _HomeState extends State<Home> {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<SignInProvider>(create: (context) => GoogleSignInProvider()),
        ChangeNotifierProvider<SignInProvider>(create: (context) => FacebookSignInProvider())
      ],
      child: Scaffold(
        body: StreamBuilder(
            stream: FirebaseAuth.instance.authStateChanges(),
            builder: (context, snapshot) {
              final provider =
                  Provider.of<SignInProvider>(context, listen: true);
              if (provider.isSigningIn) {
                return // Show loader
              } else if (snapshot.hasData) {
                return // Show logged-in UI
              } else {
                // Give option to login via Google and Facebook
                return Row(children: [
                TextButton(
                  onPressed: () {
                    final provider = Provider.of<SignInProvider>(
                      context,
                      listen: false,
                    );
                    provider.login();
                  },
                  child: const Text('Login with Google'),
                ),
                TextButton(
                  onPressed: () {
                    final provider = Provider.of<SignInProvider>(
                      context,
                      listen: false,
                    );
                    provider.login();
                  },
                  child: const Text('Login with Facebook'),
                ),
              ],
            );
          }
        }),
      ),
    );
  }
}

当前UI中,点击‘Login with Google’和‘Login with Facebook’按钮时,均指向最后注册的FacebookSignInProvider;若将onPressed中的获取方式改为Provider.of<GoogleSignInProvider>或Provider.of<FacebookSignInProvider>则会崩溃。请问如何让按钮调用对应的登录提供商?

解决方案

核心原因

你遇到的问题是:同类型的Provider会被后注册的实例覆盖。因为你用ChangeNotifierProvider<SignInProvider>注册了两个实例,Flutter无法通过父类类型区分它们,只会保留最后一个注册的FacebookSignInProvider。而直接获取子类类型崩溃,是因为你注册的是父类类型的Provider,而非子类类型。

解决方法1:注册具体子类Provider(推荐)

直接将两个登录提供商以具体子类类型注册,这样就能精准获取对应实例:

修改MultiProvider的注册代码:

MultiProvider(
  providers: [
    // 注册Google登录提供商的具体类型
    ChangeNotifierProvider<GoogleSignInProvider>(create: (context) => GoogleSignInProvider()),
    // 注册Facebook登录提供商的具体类型
    ChangeNotifierProvider<FacebookSignInProvider>(create: (context) => FacebookSignInProvider())
  ],
  child: Scaffold(/* ... 原有代码 ... */)
)

然后在按钮的onPressed中获取对应子类的Provider:

Row(children: [
  TextButton(
    onPressed: () {
      final googleProvider = Provider.of<GoogleSignInProvider>(context, listen: false);
      googleProvider.login();
    },
    child: const Text('Login with Google'),
  ),
  TextButton(
    onPressed: () {
      final fbProvider = Provider.of<FacebookSignInProvider>(context, listen: false);
      fbProvider.login();
    },
    child: const Text('Login with Facebook'),
  ),
])

解决方法2:用family参数区分同类型Provider(适合坚持用父类的场景)

如果一定要用SignInProvider作为泛型,可以通过family参数给每个实例加标识,实现区分:

修改注册代码:

MultiProvider(
  providers: [
    ChangeNotifierProvider<SignInProvider>.family(
      create: (context, type) => GoogleSignInProvider(),
      param: 'google',
    ),
    ChangeNotifierProvider<SignInProvider>.family(
      create: (context, type) => FacebookSignInProvider(),
      param: 'facebook',
    ),
  ],
  child: Scaffold(/* ... 原有代码 ... */)
)

获取时传入对应标识:

// Google登录按钮
final provider = Provider.of<SignInProvider>(context, listen: false, param: 'google');
provider.login();

// Facebook登录按钮
final provider = Provider.of<SignInProvider>(context, listen: false, param: 'facebook');
provider.login();

额外优化:统一监听登录状态

如果需要统一监听两个提供商的isSigningIn状态,可以自定义一个组合Provider:

class CombinedAuthProvider with ChangeNotifier {
  final GoogleSignInProvider googleProvider;
  final FacebookSignInProvider fbProvider;

  CombinedAuthProvider(this.googleProvider, this.fbProvider) {
    // 监听两个子Provider的状态变化
    googleProvider.addListener(notifyListeners);
    fbProvider.addListener(notifyListeners);
  }

  // 只要有一个在登录,就显示加载状态
  bool get isSigningIn => googleProvider.isSigningIn || fbProvider.isSigningIn;

  @override
  void dispose() {
    googleProvider.removeListener(notifyListeners);
    fbProvider.removeListener(notifyListeners);
    super.dispose();
  }
}

然后在MultiProvider中注册这个组合Provider:

MultiProvider(
  providers: [
    ChangeNotifierProvider<GoogleSignInProvider>(create: (context) => GoogleSignInProvider()),
    ChangeNotifierProvider<FacebookSignInProvider>(create: (context) => FacebookSignInProvider()),
    // 注册组合Provider,依赖两个子Provider
    ChangeNotifierProxyProvider2<GoogleSignInProvider, FacebookSignInProvider, CombinedAuthProvider>(
      create: (context) => CombinedAuthProvider(
        Provider.of(context, listen: false),
        Provider.of(context, listen: false),
      ),
      update: (context, google, fb, previous) => CombinedAuthProvider(google, fb),
    ),
  ],
  child: Scaffold(
    body: StreamBuilder(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        final combinedProvider = Provider.of<CombinedAuthProvider>(context);
        if (combinedProvider.isSigningIn) {
          return const Center(child: CircularProgressIndicator());
        }
        // ... 原有UI逻辑
      },
    ),
  ),
)

内容的提问来源于stack exchange,提问作者itsji10dra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 13:52:15