0

我正在尝试根据使用以下代码存储在共享首选项中的值来切换抽屉选项卡。

当不使用 memoizer 但未来的构建器永远运行时,代码工作正常。

如果我使用 memorizer,未来的构建器仍然至少运行两次(不是永远),但是 get 和 set 函数不起作用,新值不会更新,也不会通知小部件。

我需要一些方法来永远停止运行未来的构建器,并通过触发其中存在的 get 和 set 函数来相应地通知用户

通知程序类

class SwitchAppProvider extends ChangeNotifier {


  switchApp(value) async {

     
        // initialize instance of sharedpreference
        SharedPreferences prefs = await SharedPreferences.getInstance();

        prefs.setBool('key', value);

        notifyListeners();
     
  }


Future<bool?> getValue() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    final value = prefs.getBool('key');
    return value;
  }
}

抽屉

Widget _buildDrawer() {
  return ChangeNotifierProvider<SwitchAppProvider>(
    create: (context) => SwitchAppProvider(),
    child: Consumer<SwitchAppProvider>(
      builder: (context, provider, _) {
        return Container(
          width: 260,
          child: Drawer(
            child: Material(
              color: Color.fromRGBO(62, 180, 137, 1),
              child: ListView(
                children: <Widget>[
                  Container(
                    padding: AppLandingView.padding,
                    child: Column(
                      children: [
                        const SizedBox(height: 10),
                        FutureBuilder(
                          future: provider.getValue(),
                          builder: (BuildContext context,
                              AsyncSnapshot<dynamic> snapshot) {
                            if (snapshot.data == true) {
                              return _buildMenuItem(
                                text: 'widget1',
                                icon: Icons.add_business,
                                onTap: () {
                                  provider.switchApp(false);
                                },
                              );
                            } else {
                              return _buildMenuItem(
                                text: 'widget2',
                                icon: Icons.add_business,
                                onTap: () {
                                  provider.switchApp(true);
                                },
                              );
                            }
                          },
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      },
    ),
  );
}

脚手架

@override
Widget build(BuildContext context) {
  return Scaffold(
    drawer: _buildDrawer(),
  );
}

更新

我进一步分析,问题在于provider.getValue(),如果我在返回值之前使用notifyListeners() 未来构建器将永远运行

我删除了它,未来的构建器不会永远运行,但它不会更新其他小部件。

情景是

小部件 1

  1. 包含一个抽屉
  2. 有一个按钮来切换应用程序
  3. 使用共享首选项(setValue() 函数)设置点击值,并通知侦听器
  4. 在小部件 1 中,通知程序运行良好,并且在点击调用 setValue() 时更改了抽屉按钮选项。
  5. 小部件 1 中的所有内容都已解决,因为它的调用 setValue() 因此触发了 notifyListeners() 并重新渲染了小部件 1

小部件 2

  1. 仅从共享偏好中获取价值(getValue() 函数)。getValue 函数不能使用 notifyListeners(),如果使用 futurebuilder 将永远运行

  2. 小部件 2 没有设置任何值,因此它不使用 setValue() 因此它没有得到通知

  3. 当在小部件 1 中触发点击 setValue() 时,我如何通知小部件 2

即 widget1 使用 setValue() 函数设置应用程序

widget2 从 getValue() 函数中获取值并得到通知

更新 2

class SwitchAppProvider with ChangeNotifier {
   dynamic  _myValue;

  dynamic get myValue => _myValue;

   set myValue(dynamic newValue) {
     _myValue = newValue;
     notifyListeners();
   }

  setValue(value) async {
    // initialize instance of sharedpreference
    SharedPreferences prefs = await SharedPreferences.getInstance();

     await  prefs.setBool('key', value);

    notifyListeners();
  }

  SwitchAppProvider(){
    getValue();
  }

  Future<void> getValue() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
     myValue = prefs.getBool('key');


  }


}


小部件 2

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider.value(
        value: SwitchAppProvider(),

        child:  Consumer<SwitchAppProvider>(
            builder: (BuildContext context, SwitchAppProvider provider, _) {



              if (provider.myValue == true) {
                return Center(
                  child: CircularProgressIndicator(),
                );
              } else {
                return Container(
                    child: Text('provider.myValue'));
              }
            })
    );
  }
}

_buildMenuItem

  // helper widget to build item of drawer
  Widget _buildMenuItem({
    required String text,
    required IconData icon,
    required GestureTapCallback onTap,
  }) {
    final color = Colors.white;
    final hoverColor = Colors.white;

    return ListTile(
      leading: Icon(icon, color: color),
      title: Text(text, style: TextStyle(color: color, fontSize: 18)),
      hoverColor: hoverColor,
      onTap: onTap,
    );
  }

4

1 回答 1

0

“如果我使用 memorizer,未来的构建器仍然至少运行两次(不是永远),但是 get 和 set 函数不起作用,新值不会更新,也不会通知小部件。”

这是预期的行为:

An AsyncMemoizer is used when some function may be run multiple times in order to get its result, but it only actually needs to be run once for its effect. 

所以prefs.setBool('key', value);只执行第一次。

你绝对不想使用它。

如果您编辑代码以删除AsyncMemoizer,我们可以尝试进一步帮助您。

更新后编辑

你是对的,该getValue()函数不应该通知侦听器,如果这样做,那么侦听器将重建并再次请求值,这将通知侦听器,侦听器将重建并再次请求值,这......(你得到点)。

你的推理有问题。widget1并且widget2不通知,Consumer则通知。这将重建一切。代码非常复杂,可以通过删除不需要的小部件来简化很多。

我会建议你

  1. await prefs.setBool('isWhatsappBusiness', value);在通知听众之前。
  2. 看看这个答案类似的问题。

编辑 3

我不知道你做错了什么,但这有效:

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

void main() {
  runApp(const MyApp());
}

class SwitchAppProvider extends ChangeNotifier {
  switchApp(value) async {
    // initialize instance of sharedpreference
    SharedPreferences prefs = await SharedPreferences.getInstance();

    await prefs.setBool('key', value);

    notifyListeners();
  }

  Future<bool?> getValue() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    final value = prefs.getBool('key');
    return value;
  }
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        drawer: _buildDrawer(),
      ),
    );
  }

  Widget _buildDrawer() {
    return ChangeNotifierProvider<SwitchAppProvider>(
      create: (context) => SwitchAppProvider(),
      child: Consumer<SwitchAppProvider>(
        builder: (context, provider, _) {
          return SizedBox(
            width: 260,
            child: Drawer(
              child: Material(
                color: const Color.fromRGBO(62, 180, 137, 1),
                child: ListView(
                  children: <Widget>[
                    Column(
                      children: [
                        const SizedBox(height: 10),
                        FutureBuilder(
                          future: provider.getValue(),
                          builder: (BuildContext context,
                              AsyncSnapshot<dynamic> snapshot) {
                            print('Am I building?');
                            if (snapshot.data == true) {
                              return ListTile(
                                tileColor: Colors.red[200],
                                title: const Text('widget1'),
                                leading: const Icon(Icons.flutter_dash),
                                onTap: () {
                                  provider.switchApp(false);
                                },
                              );
                            } else {
                              return ListTile(
                                tileColor: Colors.green[200],
                                title: const Text('widget2'),
                                leading: const Icon(Icons.ac_unit),
                                onTap: () {
                                  provider.switchApp(true);
                                },
                              );
                            }
                          },
                        ),
                      ],
                    ),
                  ],
                ),
              ),
            ),
          );
        },
      ),
    );
  }
}

如果您仍然无法使其正常工作,那么问题出在其他地方。

编辑 4

首先,我建议您在以后的问题中更加清楚。立即编写所有需要的代码并删除不需要的小部件。避免通过以相同方式命名不同事物而产生混淆。

第二个小部件不会更新,因为它正在侦听不同的通知程序。

当你这样做

return ChangeNotifierProvider.value(
        value: SwitchAppProvider(),

Widget2您创建一个新的提供者对象时,您没有监听您在Drawer.

您需要在ChangeNotifierProvider.value小部件树中将小部件移动到更高的位置,并使用相同的:

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

void main() {
  runApp(const MyApp());
}

class SwitchAppProvider extends ChangeNotifier {
  switchApp(value) async {
    // initialize instance of sharedpreference
    SharedPreferences prefs = await SharedPreferences.getInstance();

    await prefs.setBool('key', value);

    notifyListeners();
  }

  Future<bool?> getValue() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    final value = prefs.getBool('key');
    return value;
  }
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ChangeNotifierProvider<SwitchAppProvider>(
        create: (context) => SwitchAppProvider(),
        child: Scaffold(
          appBar: AppBar(),
          drawer: _buildDrawer(),
          body: const Widget2(),
        ),
      ),
    );
  }

  Widget _buildDrawer() {
    return Consumer<SwitchAppProvider>(builder: (context, provider, _) {
      return SizedBox(
        width: 260,
        child: Drawer(
          child: Material(
            color: const Color.fromRGBO(62, 180, 137, 1),
            child: ListView(
              children: <Widget>[
                Column(
                  children: [
                    const SizedBox(height: 10),
                    FutureBuilder(
                      future: provider.getValue(),
                      builder: (BuildContext context,
                          AsyncSnapshot<dynamic> snapshot) {
                        print('Am I building?');
                        if (snapshot.data == true) {
                          return ListTile(
                            tileColor: Colors.red[200],
                            title: const Text('widget1'),
                            leading: const Icon(Icons.flutter_dash),
                            onTap: () {
                              provider.switchApp(false);
                            },
                          );
                        } else {
                          return ListTile(
                            tileColor: Colors.green[200],
                            title: const Text('widget2'),
                            leading: const Icon(Icons.ac_unit),
                            onTap: () {
                              provider.switchApp(true);
                            },
                          );
                        }
                      },
                    ),
                  ],
                ),
              ],
            ),
          ),
        ),
      );
    });
  }
}

class Widget2 extends StatelessWidget {
  const Widget2({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Consumer<SwitchAppProvider>(
      builder: (BuildContext context, SwitchAppProvider provider, _) {
        return FutureBuilder(
          future: provider.getValue(),
          builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
            print('Am I building even more ?');
            if (snapshot.data == true) {
              return const Center(
                child: CircularProgressIndicator(),
              );
            } else {
              return const Text('provider.myValue');
            }
          },
        );
      },
    );
  }
}
于 2021-12-10T14:42:05.460 回答