Intercambio de datos de Flutter InheritedWidget

Hay varios widgets en Flutter, incluida la interfaz de usuario y, por supuesto, los componentes funcionales. El componente InheritedWidget es un componente funcional en Flutter. Puede realizar el intercambio de datos entre los componentes de Flutter. La dirección de transmisión de datos es desde la parte superior del árbol de widgets. próximo.

InheritedWidget implementa el intercambio de datos de componentes

  • Dado que desea usar InheritedWidget, primero escriba un Widget para heredar InheritedWidget

Implementar ShareDataWidget

/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019/11/15 0015
/// email: [email protected]
/// des:  InheritedWidget是Flutter中非常重要的一个功能型组件,它提供了一种数据在widget树中从上到下传递、共享的方式
import 'package:flutter/material.dart';


class ShareDataWidget extends InheritedWidget  {


  final int data; //需要在子树中共享的数据,保存点击次数

  ShareDataWidget( {@required this.data,Widget child})
      :super(child:child);


  // 子树中的widget通过该方法获取ShareDataWidget,从而获取共享数据
  static ShareDataWidget of(BuildContext context){
    return context.inheritFromWidgetOfExactType(ShareDataWidget);
  }


  //继承 InheritedWidget 实现的方法 返回值 决定当data发生变化时,是否通知子树中依赖data的Widget 更新数据
  @override
  bool updateShouldNotify(ShareDataWidget oldWidget) {
    //如果返回true,则子树中依赖(build函数中有调用)本widget的子widget的`state.didChangeDependencies`会被调用
    return oldWidget.data != data;
  }
}
复制代码
  • A partir de la implementación anterior, podemos ver que el valor de retorno de updateShouldNotify determina si se debe notificar al widget dependiente de datos en el subárbol para actualizar los datos cuando los datos cambien, y se implementa el método of para facilitar que el subwidget obtenga el datos compartidos.

Probar el uso compartido de datos de ShareDataWidget

  • Hemos implementado InheritedWidget anteriormente, ahora veamos cómo usar un widget aleatorio para mostrar los datos de ShareDataWidget
 /// Created with Android Studio.
/// User: maoqitian
/// Date: 2019/11/15 0015
/// email: [email protected]
/// des:  测试 ShareDataWidget
import 'package:flutter/material.dart';
import 'package:flutter_hellow_world/InheritedWidget/ShareDataWidget.dart';

class TestShareDataWidget extends StatefulWidget {
  @override
  _TestShareDataWidgetState createState() => _TestShareDataWidgetState();
}

class _TestShareDataWidgetState extends State<TestShareDataWidget> {


  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    //上层 widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
    //如果build中没有依赖InheritedWidget,则此回调不会被调用。
    print("didChangeDependencies");
  }

  @override
  Widget build(BuildContext context) {
    //显示 ShareDataWidget 数据变化,如果build中没有依赖InheritedWidget,则此回调不会被调用。
    return Text(ShareDataWidget.of(context).data.toString());

  }
}
  • Luego cree un nuevo widget para usar ShareDataWidget, cree un botón e incremente el valor de ShareDataWidget cada vez que se haga clic en él.
/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019/11/15 0015
/// email: [email protected]
/// des:  创建一个按钮,每点击一次,就将ShareDataWidget的值自增
import 'package:flutter/material.dart';
import 'package:flutter_hellow_world/InheritedWidget/ShareDataWidget.dart';
import 'package:flutter_hellow_world/InheritedWidget/TestShareDataWidget.dart';

class InheritedWidgetTest extends StatefulWidget {
  @override
  _InheritedWidgetTestState createState() => _InheritedWidgetTestState();
}

class _InheritedWidgetTestState extends State<InheritedWidgetTest> {

  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: ShareDataWidget(
        data: count, //共享数据 data
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.only(bottom: 20.0),
              child: TestShareDataWidget()//子widget中依赖ShareDataWidget
            ),
            RaisedButton(
              child: Text("计数增加"),
              onPressed: (){ 
                setState(() {
                  ++ count;
                });
              },
            )
          ],
        ),
      ),
    );
  }
}
  • El código es muy simple. Cree un botón y agregue uno al valor de datos de ShareDataWidget cada vez que se haga clic en él. El TestShareDataWidget creado anteriormente se basa en el valor de datos de ShareDataWidget. Si los datos se comparten, su valor cambiará en consecuencia.

  • resultado de ejecución

llamada de cambio de dependencias

  • Al ejecutar el ejemplo anterior, vemos que el siguiente registro se imprimirá en el registro, lo que significa que se llama al método didChangeDependencies de TestShareDataWidget cuando se cambia el valor de los datos de ShareDataWidget. Este método rara vez se usa al escribir StatefulWidget, podemos usarlo en este método Realice algunas operaciones que consumen mucho tiempo, como la persistencia de datos, solicitudes de red, etc.
I/flutter ( 7082): didChangeDependencies
  • Si no desea llamar a didChangeDependencies para que lo llamen, también hay una manera de cambiar el método de ShareDataWidget de la siguiente manera
 // 子树中的widget获取共享数据 方法
  static ShareDataWidget of(BuildContext context){
    //return context.inheritFromWidgetOfExactType(ShareDataWidget);
    //使用 ancestorInheritedElementForWidgetOfExactType 方法当数据变化则不会调用 子widget 的didChangeDependencies 方法 
    return context.ancestorInheritedElementForWidgetOfExactType(ShareDataWidget).widget;
  }
  • Aquí puede ver que el cambio usa el método context.ancestorInheritedElementForWidgetOfExactType, y ¿por qué no se llama a este método didChangeDependencies? Mirar el código fuente es la mejor explicación, pasamos directamente al código fuente de estos dos métodos en framework.dart
/**
 * framework.dart  inheritFromWidgetOfExactType和ancestorInheritedElementForWidgetOfExactType方法源码
 */
 @override
  InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
    if (ancestor != null) {
      assert(ancestor is InheritedElement);
      return inheritFromElement(ancestor, aspect: aspect);
    }
    _hadUnsatisfiedDependencies = true;
    return null;
  }


  @override
  InheritedElement ancestorInheritedElementForWidgetOfExactType(Type targetType) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
    return ancestor;
  }
  • Obviamente, podemos ver queHeredarFromWidgetOfExactType llama al métodoHeredarFromElement más de una vez y continuar mirando el código fuente de este método.
/**
 * framework.dart  inheritFromElement方法源码
 */
 
@override
  InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
    assert(ancestor != null);
    _dependencies ??= HashSet<InheritedElement>();
    _dependencies.add(ancestor);
    ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  }
  • En este punto, todo se vuelve muy claro. El métodoHeredarFromWidgetOfExactType llama al métodoHeredarFromElement, y en este método, InheritedWidget agrega una dependencia a su widget secundario, por lo que cuando InheritedWidget cambia, el widget secundario que depende de él se actualizará y el método Call the didChangeDependencies que se acaba de mencionar, y el método ancestorInheritedElementForWidgetOfExactType no registra las dependencias con el widget secundario y, por supuesto, no llama al método didChangeDependencies.

resumen

  • Lo anterior utiliza un ejemplo simple del uso de InheritedWidget para implementar el uso de InheritedWidget, comprender la llamada didChangeDependencies y se puede decir que tiene cierta comprensión del componente InheritedWidget. A continuación, al encapsular InheritedWidget, se implementa un proveedor simple para lograr el intercambio de datos entre componentes.

Implementar componentes de intercambio de datos entre componentes

  • Como desarrollador nativo de Android, el intercambio de datos entre componentes no nos resulta desconocido. Por ejemplo, Eventbus en el desarrollo de Android puede actualizar el estado de los suscriptores de eventos y usar SharedPreferences para la persistencia de datos. También existe la implementación de Eventbus en Flutter, pero aquí usamos directamente el componente InheritedWidget proporcionado por Flutter para realizar el intercambio de datos entre componentes. El conocido núcleo de proveedor en Flutter también se implementa a través de InheritedWidget, y luego implementaremos un simple Proveedor propio.

Implementar widget heredado genérico

  • Los datos que se compartirán son variados, use genéricos para declarar los datos que se deben compartir
/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019-11-17
/// email: [email protected]
/// des:  实现InheritedWidget  保存需要共享的数据InheritedWidget

import 'package:flutter/material.dart';


class InheritedProvider<T> extends InheritedWidget{

  //共享数据  外部传入
  final T data;

  InheritedProvider({@required this.data, Widget child}):super(child:child);

  @override
  bool updateShouldNotify(InheritedProvider<T> oldWidget) {
    ///返回true,则每次更新都会调用依赖其的子孙节点的`didChangeDependencies`方法。
    return true;
  }

}

Envoltorio de widget heredado

  • A través de la implementación anterior, puede ver que no hay forma en InheritedProvider de permitir que la persona que llama obtenga el componente InheritedWidget. No se preocupe, aquí hay dos puntos que deben aclararse: primero, la notificación de actualización de datos usa ChangeNotifier ( una suscripción de editor de estilo Flutter proporcionada por FlultterSDK). Clase de modo de suscriptor) para notificar y, en segundo lugar, después de recibir la notificación, el suscriptor se actualiza para reconstruir el InheritedProvider.
/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019-11-17
/// email: [email protected]
/// des:  订阅者

import 'package:flutter/material.dart';
import 'package:flutter_theme_change/provider/InheritedProvider.dart';

// 该方法用于在Dart中获取模板类型
Type _typeOf<T>(){
  return T;
}
class ChangeNotifierProvider<T extends ChangeNotifier> extends StatefulWidget{


  final Widget child;
  final T data;

  ChangeNotifierProvider({Key key,this.child,this.data});


  //方便子树中的widget获取共享数据
  static T of<T> (BuildContext context,{bool listen = true}){ //listen 是否注册依赖关系 默认注册
    final type = _typeOf<InheritedProvider<T>>();
    final provider = listen ? context.inheritFromWidgetOfExactType(type) as InheritedProvider<T> :
    context.ancestorInheritedElementForWidgetOfExactType(type)?.widget as InheritedProvider<T>;
    return provider.data;

  }


  @override
  State<StatefulWidget> createState() {
    return _ChangeNotifierProviderState<T>();
  }
}

class _ChangeNotifierProviderState<T extends ChangeNotifier> extends State<ChangeNotifierProvider<T>>{

  @override
  Widget build(BuildContext context) {
  //构建 InheritedProvider
    return InheritedProvider<T>(
      data: widget.data,
      child: widget.child,
    );
  }
}
  • A partir del código anterior, se crea un StatefulWidget, y la compilación final es InheritedProvider. En este momento, se crea el método of que devuelve los datos de datos correspondientes, y si el control secundario está vinculado a InheritedWidget se puede establecer configurando (ya analizado en la sección anterior), por lo que los Controles que cambian los datos pueden no vincularse de manera flexible a InheritedWidget, y no hay necesidad de actualizar los widgets de control que cambian los datos cada vez.
  • Luego mejoramos _ChangeNotifierProviderState. Cuando los controles externos actualizan los datos y notifican la actualización a través de ChangeNotifier, ChangeNotifierProvider puede actualizarse para que los nuevos datos surtan efecto. La forma de actualizar es usar el método setState, que también es el propósito de crear StatefulWidget.
class _ChangeNotifierProviderState<T extends ChangeNotifier> extends State<ChangeNotifierProvider<T>>{

  @override
  void initState() {
    // 给model添加监听器
    widget.data.addListener(update);
    super.initState();
  }


  @override
  void didUpdateWidget(ChangeNotifierProvider<T> oldWidget) {
    //当Provider更新时,如果新旧数据不"==",则解绑旧数据监听,同时添加新数据监听
    if(widget.data != oldWidget.data){
       oldWidget.data.removeListener(update);
       widget.data.addListener(update);
    }
    super.didUpdateWidget(oldWidget);
  }

  // build方法 省略
  ........

  @override
  void dispose() {
    // 移除model监听器
    widget.data.removeListener(update);
    super.dispose();
  }

  void update() {
    //如果数据发生变化(model类调用了notifyListeners),重新构建InheritedProvider
    setState(() => {

    });
  }

Paquete de consumo de datos (Consumidor)

  • Se actualizan los datos, se envía el mensaje, y alguien tiene que consumirlo, para que el modelo suscriptor-consumidor sea completo, dicho sin rodeos, el número de consumo es llamar al método of de ChangeNotifierProvider para obtener nuevos datos. el paso anterior, hemos disparado la actualización del suscriptor, e indirectamente reconstruirá su sub-widget, y la reconstrucción del sub-widget corresponde a los datos de consumo, porque el consumidor depende del propio suscriptor, mira el código
/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019/11/18 0018
/// email: [email protected]
/// des:  事件 消费者 获得当前context和指定数据类型的Provider
import 'package:flutter/material.dart';
import 'package:flutter_theme_change/provider/ChangeNotifierProvider.dart';

class Consumer<T> extends StatelessWidget{

  final Widget child;
  //获得当前context
  final Widget Function(BuildContext context, T value) builder;

  Consumer({Key key,@required this.builder,this.child}):assert(builder !=null),super(key:key);


  @override
  Widget build(BuildContext context) {  //默认绑定 注册依赖关系
    return builder(context,ChangeNotifierProvider.of<T>(context)); //自动获取Model 获取更新的数据
  }

}
  • A partir del código anterior, la compilación del Consumidor llama al método ChangeNotifierProvider.of para registrar la dependencia de forma predeterminada, por lo que el widget implementado por el Consumidor actualizará los datos mediante la función de InheritedWidget.

resumen

  • El resumen anterior puede ser reemplazado por un diagrama de flujo

 

Componentes para compartir datos PracticeToggle Topic

  • En la sección anterior, escribí un componente de uso compartido de datos de proveedor muy simple basado en InheritedWidget. A continuación, uso el ChangeNotifierProvider recién escrito a través de un ejemplo de cambio de temas.

  • El cambio de tema aquí simplemente cambia el color del tema, por lo que los datos compartidos son el valor del color. La idea de demostración es usar Dialog para proporcionar un color de tema seleccionable y luego hacer clic en el color correspondiente para cambiar el color del tema de la aplicación y luego implementarlo. juntos.

Crear un modelo de tema

  • El modelo también se puede ver como datos compartidos, heredando ChangeNotifier, por lo que puede llamar al método de notificaciónListeners para activar ChangeNotifierProvider para recibir notificaciones de cambio de datos.
/// Created with Android Studio.
/// User: maoqitian
/// Date: 2019/11/18 0018
/// email: [email protected]
/// des:  主题 model
import 'package:flutter/material.dart';

class ThemeModel extends ChangeNotifier {
  int settingThemeColor ;
  ThemeModel(this.settingThemeColor);

  void changeTheme (int themeColor){
    this.settingThemeColor = themeColor;
    // 通知监听器(订阅者),重新构建InheritedProvider, 更新状态。
    notifyListeners();
  }
}

MaterialApp como widget secundario ChangeNotifierProvider

  • Cambie el color del tema, es decir, la propiedad del tema de MaterialApp, para que MaterialApp se use como el subwidget ChangeNotifierProvider, para que MaterialApp pueda recibir el valor de datos de color del tema compartido.
class _MyHomePageState extends State<MyHomePage> {

  int themeColor =0;
  
  @override
  void initState() {
    super.initState();
    themeColor = sp.getInt(SharedPreferencesKeys.themeColor);
    if(themeColor == null ){
      themeColor = 0xFF3391EA;//默认蓝色
    }
  }
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ChangeNotifierProvider<ThemeModel>(
        data: ThemeModel(themeColor),
        child: Consumer<ThemeModel>(
          builder: (BuildContext context,themeModel){
            return MaterialApp(
              theme: ThemeData(
                primaryColor: Color(themeModel.settingThemeColor),
              ),
              home: Scaffold(
                  appBar: AppBar(
                    title: Text("Flutter Theme Change"),
                    actions: <Widget>[
                      Builder(builder: (context){
                        return IconButton(icon: new Icon(Icons.color_lens), onPressed: (){
                          _changeColor(context);
                        });
                      },)
                      // onPressed 点击事件
                    ],
                  ),
                  body: Center(
                    child: Text("主题变化测试"),
                  )
              ),
            );
          },
        ),
      ),
    );
  }

  void _changeColor(BuildContext context) {
      buildSimpleDialog(context);
  }
  • Agregue IconButton a la barra de aplicaciones para que pueda hacer clic para mostrar el cuadro de diálogo de selección de color. El cuadro de diálogo muestra un widget de matriz de valores de color. Cada widget se implementa de la siguiente manera
class SingleThemeColor extends StatelessWidget {

  final int themeColor;
  final String colorName;

  const SingleThemeColor({Key key,this.themeColor, this.colorName}):
        super(key:key);

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () async{
         print("点击了改变主题");
         //改变主题
         ChangeNotifierProvider.of<ThemeModel>(context,listen: false).changeTheme(this.themeColor);
         await SpUtil.getInstance()..putInt(SharedPreferencesKeys.themeColor, this.themeColor);
         Navigator.pop(context);
      },
      child: new Column( // 竖直布局
        children: <Widget>[
           Container(
             width: 50,
             height: 50,
             margin: const EdgeInsets.all(5.0),
             decoration: BoxDecoration( //圆形背景装饰
               borderRadius:BorderRadius.all(
                  Radius.circular(50)
               ),
               color: Color(this.themeColor)
             ),
           ),
           Text(
             colorName,
             style: TextStyle(
               color: Color(this.themeColor),
               fontSize: 14.0),
           ),
        ],
      ),
    );
  }
}
  • Puede ver que cada clic en el widget responde a onTap y llama a ChangeNotifierProvider.of para obtener el objeto ThemeModel y llamar al método changeTheme para activar el método notificarListeners. También hay algunos detalles, como guardar código como valores de color a través de SharedPreferences.Para más detalles, puede consultar la dirección del código fuente del proyecto de demostración al final del artículo.
  • Efecto de ejecución de demostración

 

finalmente

  • Al ver esto, creo que debería tener una mejor comprensión de InheritedWidget, comprender el principio y usar las ruedas más fácilmente. Si desea utilizar el uso compartido de datos entre componentes, debe utilizar directamente el Proveedor con todas las funciones . Se ha completado otro artículo. Creo que será útil para usted que lea el artículo. Si hay algún error en el artículo, indíquemelo. Aprendamos y progresemos juntos. Si cree que mi artículo lo ayuda, por favor dame también Un me gusta y un seguimiento, pero también bienvenido a visitar mi blog personal .
  • Flutter proyecto completo de código abierto:  https://github.com/maoqitian/flutter_wanandroid

Sitio de demostración

Supongo que te gusta

Origin blog.csdn.net/jdsjlzx/article/details/123338549
Recomendado
Clasificación