【问题标题】:Flutter Inherited Widget - Missing some listenerFlutter Inherited Widget - 缺少一些监听器
【发布时间】:2018-12-26 01:05:49
【问题描述】:

我正在尝试使以下工作。应用程序模型状态的更改不会通过 InheritedWidget 'AppStateProvider' 获取。我已经设法让它与接收器/流一起工作,但希望建立一个更简单的结构。

这只是一个在各种应用模式之间切换的测试应用。

缺少什么?

import 'package:flutter/material.dart';

void main() {
  runApp(AppStateProvider(
    child: RootPage(),
    appState: new AppState(),
  ));
}

enum AppMode { introduction, login, home }

class AppState {
  AppMode appMode;
  AppState({
    this.appMode = AppMode.introduction,
  });
}

class AppStateProvider extends InheritedWidget {
  final AppState appState;

  AppStateProvider({Key key, Widget child, this.appState})
      : super(key: key, child: child);

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) => true;

  static AppStateProvider of(BuildContext context) {
    return (context.inheritFromWidgetOfExactType(AppStateProvider)
        as AppStateProvider);
  }
}

class RootPage extends StatelessWidget {
  AppMode _mode;

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Inherited Widget Test',
      theme: new ThemeData(
        primarySwatch: Colors.blueGrey,
      ),
      home: _body(context),
    );
  }

  Widget _body(BuildContext context) {
    final provider = AppStateProvider.of(context); //Registers as a listener
    final state = provider.appState;
    _mode = state.appMode;

    return new Stack(
      children: <Widget>[
        new Offstage(
          offstage: _mode != AppMode.introduction,
          child: new MaterialApp(
            home: ColorsListPage(
              color: Colors.red,
              targetAppMode: AppMode.login,
              title: "Intro",
            ),
          ),
        ),
        new Offstage(
          offstage: _mode != AppMode.login,
          child: new MaterialApp(
            home: ColorsListPage(
              color: Colors.blue,
              targetAppMode: AppMode.home,
              title: "Login",
            ),
          ),
        ),
        new Offstage(
          offstage: _mode != AppMode.home,
          child: new MaterialApp(
            home: ColorsListPage(
              color: Colors.green,
              targetAppMode: AppMode.introduction,
              title: "Home",
            ),
          ),
        ),
      ],
    );
  }
}

class ColorDetailPage extends StatefulWidget {
  final String title;
  final MaterialColor color;
  final int materialIndex;
  final AppMode targetAppMode;

  ColorDetailPage(
      {this.color, this.title, this.targetAppMode, this.materialIndex: 500});

  @override
  _ColorDetailPageState createState() => new _ColorDetailPageState();
}

class _ColorDetailPageState extends State<ColorDetailPage> {
  @override
  Widget build(BuildContext context) {
    final provider = AppStateProvider.of(context);

    return Scaffold(
      appBar: AppBar(
        backgroundColor: widget.color,
        title: Text(
          '$widget.title[$widget.materialIndex]',
        ),
      ),
      body: Container(
        color: widget.color[widget.materialIndex],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            provider.appState.appMode = widget.targetAppMode;
          });
        },
        heroTag: null,
      ),
    );
  }
}

class ColorsListPage extends StatefulWidget {
  final MaterialColor color;
  final String title;
  final ValueChanged<int> onPush;
  final AppMode targetAppMode;
  final List<int> materialIndices = [
    100,
    200,
    300,
    400,
    500,
    600,
    700,
    800,
    900,
  ];

  ColorsListPage({this.color, this.targetAppMode, this.title, this.onPush});

  @override
  _ColorsListPageState createState() => new _ColorsListPageState();
}

class _ColorsListPageState extends State<ColorsListPage> {
  @override
  Widget build(BuildContext context) {
    final provider = AppStateProvider.of(context);

    return new Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
          backgroundColor: widget.color,
        ),
        body: Container(
          color: Colors.white,
          child: _buildList(context),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            setState(() {
              provider.appState.appMode = widget.targetAppMode;
            });
          },
          heroTag: null,
        ));
  }

  Widget _buildList(BuildContext context) {
    return ListView.builder(
      itemCount: widget.materialIndices.length,
      itemBuilder: (BuildContext content, int index) {
        int materialIndex = widget.materialIndices[index];
        return Container(
            color: widget.color[materialIndex],
            child: ListTile(
                title: Text(
                  "$materialIndex",
                  style: TextStyle(fontSize: 24.0),
                ),
                trailing: Icon(Icons.chevron_right),
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                        builder: (context) => ColorDetailPage(
                              color: widget.color,
                              title: widget.title,
                              targetAppMode: widget.targetAppMode,
                              materialIndex: materialIndex,
                            )),
                  );
                }
                //onTap: () => onPush(materialIndex),
                ));
      },
    );
  }
}

【问题讨论】:

    标签: flutter state inherited-widget


    【解决方案1】:

    您需要将您的 InheritedWidget 包装在 StatefulWidget

    class _AppStateProvider extends InheritedWidget {
      final AppStateProviderState data;
    
      _AppStateProvider({Key key, @required Widget child, @required this.data})
          : super(key: key, child: child);
    
      @override
      bool updateShouldNotify(InheritedWidget oldWidget) => true;
    }
    
    class AppStateProvider extends StatefulWidget {
      final Widget child;
      final AppState appState;
    
      AppStateProvider({
        @required this.child,
        @required this.appState,
      });
    
      static AppStateProviderState of(BuildContext context) {
        return (context.inheritFromWidgetOfExactType(_AppStateProvider)
                as _AppStateProvider)
            .data;
      }
    
      @override
      AppStateProviderState createState() => AppStateProviderState(
            appState,
          );
    }
    
    class AppStateProviderState extends State<AppStateProvider> {
      AppState appState;
    
      AppStateProviderState(this.appState);
    
      void updateAppMode(AppMode appMode) {
        setState(() {
          appState.appMode = appMode;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return _AppStateProvider(
          data: this,
          child: widget.child,
        );
      }
    }
    

    for more information

    注意这个方法:

      void updateAppMode(AppMode appMode) {
        setState(() {
          appState.appMode = appMode;
        });
      }
    

    你可以这样使用它:

      floatingActionButton: FloatingActionButton(
        onPressed: () {
          provider.updateAppMode(widget.targetAppMode);
        },
        heroTag: null,
      ),
    

    【讨论】:

    • 谢谢。我确实得到了类似的工作。使用继承的小部件并不总是需要包装在有状态的小部件中。所以我很想知道为什么这里需要它。我希望有一个更简单的解决方案。相对于 Streams 和 sinks 选项,这是很多样板代码。
    • @CraigJarman 您需要替换 InheritedWidget 对象来重建小部件树。因此,您必须将StatesetState 方法一起使用。或者,您可以使用ScopedModelproandroiddev.com/…
    猜你喜欢
    • 2018-09-04
    • 2022-10-14
    • 1970-01-01
    • 2023-03-07
    • 2021-02-17
    • 1970-01-01
    • 2021-12-14
    • 2014-06-07
    • 1970-01-01
    相关资源
    最近更新 更多