即使状态数据发生变化,Flutter InheritedWidget 也不会重建它的子组件

问题描述 投票:0回答:1

这是我的 InheritedWidget,

class AuthViewInherited extends InheritedWidget {
  const AuthViewInherited({
    super.key,
    required super.child,
    required this.data,
  });

  final AuthViewProviderState data;

  static AuthViewProviderState of(BuildContext context) {
    final result = context.dependOnInheritedWidgetOfExactType<AuthViewInherited>();
    if (result == null) {
      throw Exception("Could not find AuthViewInherited");
    }

    return result.data;
  }

  @override
  bool updateShouldNotify(covariant AuthViewInherited oldWidget) {
    return oldWidget.data.isBottomSheetOpen != data.isBottomSheetOpen;
  }
}

这是我的国课,

class AuthViewProvider extends StatefulWidget {
  const AuthViewProvider({super.key, required this.child});

  final Widget child;

  @override
  State<AuthViewProvider> createState() => AuthViewProviderState();
}

class AuthViewProviderState extends State<AuthViewProvider> {
 
  bool isBottomSheetOpen = false;

  void changeBottomSheetOpen(bool v) {
    setState(() {
      isBottomSheetOpen = v;
    });
  }

  @override
  Widget build(BuildContext context) {
    return AuthViewInherited(data: this, child: widget.child);
  }

当我像这样在我的视图中使用它时,我在第一帧中看到“false”。但是当我在AuthView中触发changeBottomSheetOpen()时我就看不到它了。当我将其更改为 updateShouldNotify => true 时它会起作用,但我不想这样做。为什么 oldWidget.data.isBottomSheetOpen != data.isBottomSheetOpen 不起作用?

class AuthView extends StatefulWidget {
  const AuthView({super.key});

  @override
  State<AuthView> createState() => _AuthViewState();
}

class _AuthViewState extends State<AuthView> {
  @override
  Widget build(BuildContext context) {
    final authViewProvider = AuthViewInherited.of(context);
    print(authViewProvider.isBottomSheetOpen);

    // I expect rebuild in here when I call **changeBottomSheetOpen(true)**, but not rebuild.

flutter state-management inherited-widget
1个回答
0
投票

我最好的猜测是,在

AuthViewInherited
中创建的
AuthViewProviderState
仅依赖于
AuthViewProviderState
实例 (
data: this
)。当
isBottomSheetOpen
更改时,
AuthViewInherited
不会重建,因为它不依赖于该字段。

最快的修复方法 - 是向

AuthViewInherited
添加一个新字段以反映
isBottomSheetOpen
。换句话说,添加新的
bool
字段,并在
updateShouldNotify
中使用它。像这样的东西:

class AuthViewInherited extends InheritedWidget {
  const AuthViewInherited({
    super.key,
    required super.child,
    required this.data,
    required this.someBool,
  });

  final AuthViewProviderState data;
  // new field
  final bool someBool;

  static AuthViewProviderState of(BuildContext context) {
    final result =
        context.dependOnInheritedWidgetOfExactType<AuthViewInherited>();
    if (result == null) {
      throw Exception("Could not find AuthViewInherited");
    }

    return result.data;
  }

  @override
  bool updateShouldNotify(covariant AuthViewInherited oldWidget) {
    // add one more condition to check. Or, maybe, check only someBool.
    return oldWidget.data.isBottomSheetOpen != data.isBottomSheetOpen ||
        oldWidget.someBool != someBool;
  }
}

并且还更新

AuthViewProviderState

class AuthViewProviderState extends State<AuthViewProvider> {
  bool isBottomSheetOpen = false;

  void changeBottomSheetOpen(bool v) {
    setState(() {
      isBottomSheetOpen = v;
    });
  }

  @override
  Widget build(BuildContext context) {
    return AuthViewInherited(
      data: this,
      child: widget.child,
      someBool: isBottomSheetOpen,
    );
  }
}

但是,在我看来,当前的选择仍然有缺点,很难理解,而且仍然容易出错。我认为最好将

AuthViewInherited
AuthViewProviderState
分开,这样它们就不会互相引用。为此,您可以在 State 中创建另一个
of
静态方法:

class AuthViewProviderState extends State<AuthViewProvider> {
  bool isBottomSheetOpen = false;

  void changeBottomSheetOpen(bool v) {
    setState(() {
      isBottomSheetOpen = v;
    });
  }

  @override
  Widget build(BuildContext context) {
    return AuthViewInherited(
      // we get rid of data parameter in InheritedWidget.
      child: widget.child,
      someBool: isBottomSheetOpen,
    );
  }

  // This is new method to get AuthViewProviderState down the tree.
  static AuthViewProviderState of(BuildContext context) {
    final result = context.findAncestorStateOfType<AuthViewProviderState>();
    if (result == null) {
      throw Exception("Could not find AuthViewInherited");
    }

    return result;
  }
}

_AuthViewState 
内部的用法将如下所示:

class AuthView extends StatefulWidget {
  const AuthView({super.key});

  @override
  State<AuthView> createState() => _AuthViewState();
}

class _AuthViewState extends State<AuthView> {
  // We will create new field here and populate it in didChangeDependencies, because it's like best practice and I do not see reason to not use it :)
  late bool shouldBottomSheetBeOpen;

  // Here we will populate shouldBottomSheetBeOpen. read about this method in docs, but in two words: it's created specifically to use with InheritedWidget.
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    shouldBottomSheetBeOpen = AuthViewInherited.of(context).someBool;
  }

  @override
  Widget build(BuildContext context) {
    // This line is not needed anymore that's why I commented it.
    // You should use shouldBottomSheetBeOpen as a state in build method.
    // final authViewProvider = AuthViewInherited.of(context);

    final authViewProviderState = AuthViewProviderState.of(context);
    print('isBottomSheetOpen: ${shouldBottomSheetBeOpen}');

在这种情况下,您仍然可以从

changeBottomSheetOpen()
AuthViewProviderState
访问
authViewProviderState. changeBottomSheetOpen()
方法,但现在您的设置将更加独立,并且更容易管理,以防您需要更新某些内容。

© www.soinside.com 2019 - 2024. All rights reserved.