首页 > 解决方案 > 什么是正确的提供者为我的 DrawerNavigation 小部件?

问题描述

我正在开发一个具有 Google 登录功能的简单移动应用程序。

到目前为止,该应用程序一直在运行,但在最新步骤(实际登录)中,我收到以下错误

════════ Exception caught by widgets library ═══════════════════════════════════
Error: Could not find the correct Provider<AuthBlock> above this DrawerNavigation Widget

我已经尝试过通常的嫌疑人;确保正在导入正确的包,这是由于我在虚拟移动设备上运行,并且可能需要热重启。然而问题依然存在。

我原以为它会提示用户使用 Google 登录,但这显然没有发生。

我的代码如下,我相信错误来自第 54 行,它读取final authBlock = Provider.of(context); .

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:to_do_list/blocks/auth_block.dart';
import 'package:to_do_list/screens/home_screen.dart';
import 'package:to_do_list/screens/categories_screen.dart';
import 'package:to_do_list/screens/todos_by_category.dart';
import 'package:to_do_list/service/category_service.dart';
import 'package:flutter_signin_button/flutter_signin_button.dart';

class DrawerNavigation extends StatefulWidget {
  @override
  _DrawerNavigationState createState() => _DrawerNavigationState();
}

class _DrawerNavigationState extends State<DrawerNavigation> {
  //List<Widget> _categoryList = List<Widget>(); //Has been depricated
  List<Widget> _categoryList = List<Widget>.generate(0, (index) {
    //Widget obj = Widget();
    //obj.id = index;
    return;
  });

  CategoryService _categoryService = CategoryService();

  @override
  initState() {
    super.initState();
    getAllCategories();
  }

  getAllCategories() async {
    var categories = await _categoryService.readCategories();

    categories.forEach((category) {
      setState(() {
        _categoryList.add(InkWell(
          onTap: () => Navigator.push(
              context,
              new MaterialPageRoute(
                  builder: (context) => new TodosByCategory(
                        category: category['name'],
                      ))),
          child: ListTile(
            title: Text(category['name']),
          ),
        ));
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    final authBlock = Provider.of<AuthBlock>(context);
    return Container(
      child: Drawer(
        child: ListView(
          children: <Widget>[
            // UserAccountsDrawerHeader(
            //   currentAccountPicture: CircleAvatar(
            //       backgroundImage: NetworkImage(
            //           'https://cdn.shopify.com/s/files/1/1733/6579/products/product-image-602960373_1024x1024@2x.jpg')),
            //   accountName: Text('Meena Bobeena'),
            //   accountEmail: Text('meena@happycat.com'),
            //   decoration: BoxDecoration(color: Colors.blue),
            // ),
            Column(
              children: [
                SignInButton(
                  Buttons.Google,
                  onPressed: () => authBlock.loginWithGoogle(),
                ),
              ],
            ),
            ListTile(
              leading: Icon(Icons.home),
              title: Text('Home'),
              onTap: () => Navigator.of(context).push(
                  MaterialPageRoute(builder: (context) => (HomeScreen()))),
            ),
            ListTile(
              leading: Icon(Icons.view_list),
              title: Text('Categories'),
              onTap: () => Navigator.of(context).push(MaterialPageRoute(
                  builder: (context) => (CategoriesScreen()))),
            ),
            Divider(),
            Column(
              children: _categoryList,
            )
          ],
        ),
      ),
    );
  }
}

根据要求,以下是 DrawerNavigation 的调用方式。

import 'package:flutter/material.dart';
import 'package:to_do_list/helpers/drawer_navigation.dart';
import 'package:to_do_list/screens/todo_screen.dart';
import 'package:to_do_list/service/todo_service.dart';
import 'package:to_do_list/models/todo.dart';

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  TodoService _todoService;

  //List<Todo> _todoList = List<Todo>(); // Has been deprecated
  List<Todo> _todoList = List<Todo>.generate(0, (index) {
    Todo obj = Todo();
    obj.id = index;
    return obj;
  });

  @override
  initState() {
    super.initState();
    getAllTodos();
  }

  getAllTodos() async {
    _todoService = TodoService();
    //_todoList = List<Todo>(); //Has been depricated
    _todoList = List<Todo>.generate(0, (index) {
      Todo obj = Todo();
      obj.id = index;
      return obj;
    });

    var todos = await _todoService.readTodos();

    todos.forEach((todo) {
      setState(() {
        var model = Todo();
        model.id = todo['id'];
        model.title = todo['title'];
        model.description = todo['description'];
        model.category = todo['category'];
        model.todoDate = todo['todoDate'];
        model.isFinished = todo['isFinished'];
        _todoList.add(model);
      });
    });
  }

  _deleteFormDialog(BuildContext context, categoryId) {
    return showDialog(
        context: context,
        barrierDismissible: true,
        builder: (param) {
          return AlertDialog(
            actions: <Widget>[
              TextButton(
                style: ButtonStyle(
                    backgroundColor: MaterialStateProperty.all(Colors.red),
                    foregroundColor: MaterialStateProperty.all(Colors.white)),
                onPressed: () async {
                  Navigator.pop(context);
                },
                child: Text('Cancel'),
              ),
              TextButton(
                style: ButtonStyle(
                    backgroundColor: MaterialStateProperty.all(Colors.green),
                    foregroundColor: MaterialStateProperty.all(Colors.white)),
                // This doesn't delete :'(
                onPressed: () async {
                  var result = await _todoService.deleteTodos(categoryId);
                  //print(result);
                  if (result > 0) {
                    print(result);
                    Navigator.pop(context);
                    getAllTodos();
                    _showSuccessSnackBar(Text('Deleted'));
                  }
                },
                child: Text('Delete'),
              ),
            ],
            title: Text('Are you sure you wish to delete this To Do item ?'),
          );
        });
  }

  _showSuccessSnackBar(message) {
    var _snackBar = SnackBar(content: message);
    ScaffoldMessenger.of(context).showSnackBar(_snackBar);
    //_globalKey.currentState.showSnackBar(_snackBar); === Depreciated
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Meenas To Do List')),
      drawer: DrawerNavigation(),
      body: ListView.builder(
          itemCount: _todoList.length,
          itemBuilder: (context, index) {
            return Padding(
              padding: const EdgeInsets.only(top: 8, left: 8, right: 8),
              child: Card(
                elevation: 8,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(16),
                ),
                child: ListTile(
                  title: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: <Widget>[
                      Text(_todoList[index].title ?? 'No Title'),
                      IconButton(
                        icon: Icon(Icons.delete, color: Colors.red),
                        onPressed: () {
                          _deleteFormDialog(context, _todoList[index].id);
                        },
                      )
                    ],
                  ),
                  subtitle: Text(_todoList[index].category ?? 'No Category'),
                  trailing: Text(_todoList[index].todoDate ?? 'No Date'),
                ),
              ),
            );
          }),
      floatingActionButton: FloatingActionButton(
          onPressed: () => Navigator.of(context)
              .push(MaterialPageRoute(builder: (context) => TodoScreen())),
          child: Icon(Icons.add)),
    );
  }
}

这是我实例化提供程序的代码

import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:to_do_list/service/auth_service.dart';

class AuthBlock {
  final authService = AuthService();
  final googleSignIn = GoogleSignIn(scopes: ['email']);

  Stream<User> get currentUser => authService.currentUser;

  loginWithGoogle() async {
    try {
      final GoogleSignInAccount googleUser = await googleSignIn.signIn();
      final GoogleSignInAuthentication googleAuth =
          await googleUser.authentication;
      final AuthCredential credential = GoogleAuthProvider.credential(
        idToken: googleAuth.idToken,
        accessToken: googleAuth.accessToken,
      );

      //Firebase Sign In
      final result = await authService.signInWithCredential(credential);

      print('${result.user.displayName}');
    } catch (error) {
      print(error);
    }
  }

  logout() {
    authService.logout();
  }
}

main.dart

import 'package:flutter/material.dart';
import 'package:to_do_list/src/app.dart';

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

标签: flutterandroid-contentprovidergoogle-signin

解决方案


您是否使用提供商打包了您的应用程序?例如,就像你在这里看到的

runApp(
    /// Providers are above [MyApp] instead of inside it, so that tests
    /// can use [MyApp] while mocking the providers
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => Counter()),
      ],
      child: const MyApp(),
    ),
  );

推荐阅读