【问题标题】:Subclass a class that extends StatelessWidget or StatefulWidget class子类化扩展 StatelessWidget 或 StatefulWidget 类的类
【发布时间】:2018-12-30 18:06:41
【问题描述】:

是否可以创建一个扩展StatelessWidget或StatefulWidget的类。

例如:

class MyButton extends StatelessWidget {
final String label;
Button({this.label});
@override
Widget build(BuildContext context) {
    return ButtonExample("label");}
}

然后

class SubmitButton extends MyButton
{
   String label;
   SubmitButton({Key key, this.label}) : super(label: label);

// then somehow extend the parent build and change only the color
// or in case of StatefulWidget change a functionality
}

我尝试在网上搜索示例,但没有成功。

【问题讨论】:

  • 这通常不是一个好主意。而是使用组合而不是继承。
  • @GünterZöchbauer 好的,这可能就是我没有找到这样做的例子的原因。请发表您的评论作为答案。
  • 查看TextFormField 以获取如何扩展的示例(是的,即使是 Flutter 开发人员偶尔也会扩展 StatelessWidget 以外的小部件)。它可以具有扩展价值。只是不要过度使用它。

标签: flutter


【解决方案1】:

在 Flutter 中,组合优于继承。
小部件不应该被扩展,这就是为什么没有示例或教程如何做到这一点。

Flutter 非常注重组合,并且包含的​​小部件库包含许多较小的小部件,它们可以很好地完成一件事,允许以多种不同的方式将它们组合成自定义小部件。

【讨论】:

  • 然后假设我想通过更改onTap 方法实现来扩展BottomNavBar 的功能,但保持所有与样式相关的内容相同。我应该如何将它变成一个通用的小部件,我可以轻松地在任何地方使用,而不需要一个一个地获取BottomNavBar 的每一个属性?
【解决方案2】:

正如 Gunter 所说,flutter 使用组合而不是继承。

官方来源:flutter faq

Flutter 不是让每个小部件提供大量参数,而是包含组合。小部件由较小的小部件构建而成,您可以以新颖的方式重复使用和组合这些小部件来制作自定义小部件。例如,RaisedButton 不是继承通用按钮小部件,而是将 Material 小部件与 GestureDetector 小部件组合在一起。 Material 小部件提供视觉设计,GestureDetector 小部件提供交互设计。

这意味着您应该创建一个较小的小部件,而不是扩展一个小部件,然后重用它。

一个实际的例子是基本按钮:

class MyButton extends StatelessWidget {
  final Color color;

  MyButton({this.color = Colors.grey, Key key}): super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: color,
      child: Text("My Button"),
    );
  }
}

然后使用组合重用以创建更具体类型的按钮:

class OutlineButton extends StatelessWidget {
  final Color color;

  OutlineButton({this.color = Colors.grey, Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return DecoratedBox(
      decoration: BoxDecoration(
        border: Border.all(
          color: color,
          width: 2.0,
          style: BorderStyle.solid,
        ),
      ),
      child: MyButton(
        color: color,
      ),
    );
  }
}

【讨论】:

  • 如果我有一个定义全局样式的 customTextField,然后是一个使用 customTextField 作为样式但需要 TextFormField 的某些属性(如键盘类型)的 emailTextField,该怎么办?无需在 customTextField 中显式传递此属性?
【解决方案3】:

如果您强烈需要扩展已经扩展 StatefulWidget 的小部件,您可以执行以下操作:

class WidgetFoo extends StatefulWidget {
  final String varFromFoo = 'foo';
  @override
  State<StatefulWidget> createState() => WidgetFooState<WidgetFoo>();
}

// Don't make this class name private (beginning with _) to allow its usage in other modules.
class WidgetFooState <T extends StatefulWidget> extends State<T> {
  String varFromFooState = 'foo state';
  @override
  Widget build(BuildContext context) {
    return Text(getText());
  }

  String getText() {
    return 'WidgetFoo';
  }
}

class WidgetBar extends WidgetFoo {
  @override
  State<StatefulWidget> createState() => _WidgetBarState<WidgetBar>();
}

class _WidgetBarState extends WidgetFooState<WidgetBar> {
  @override
  String getText() {
    return 'WidgetBar, ${varFromFooState}, ${widget.varFromFoo}';
  }
}

如果您实例化 WidgetBar,它将使用祖先的变量呈现 WidgetBar, foo state, foo 文本。

这不是在 Flutter 上进行开发的最佳方式,但仍然可以直接回答您的问题。无状态小部件的扩展是类似的。您只需添加返回一些默认值并且可以在继承的类中覆盖的方法。这就是 OOP 的经典。

【讨论】:

    【解决方案4】:

    您可以使用混合。查看 dart 文档或 Flutter 文档以了解如何使用它们。使用 mixins,您可以将属性/函数添加到现有类。您可能已经在动画控制器中看到了一个示例。

    【讨论】:

      【解决方案5】:

      在我看来,扩展StatefulWidget 和相应的State 的有效用例之一是处理Streams。我更喜欢有一个处理重新订阅的基类,而不是一遍又一遍地重复相同的代码。

      abstract class DataRepositoryConsumer extends StatefulWidget {
        const DataRepositoryConsumer({Key? key, required this.dataRepository}) : super(key: key);
      
        final DataRepository dataRepository;
      }
      
      abstract class DataRepositoryConsumerState<T extends DataRepositoryConsumer> extends State<T> {
        @override
        void initState() {
          subscribeToStreams();
          super.initState();
        }
      
        @override
        void didUpdateWidget(covariant T oldWidget) {
          if (widget.dataRepository != oldWidget.dataRepository) {
            subscribeToStreams();
          }
          super.didUpdateWidget(oldWidget);
        }
      
        // method used to get the streams from the dataRepository and store them in the state
        void subscribeToDataStreams();
      }
      

      【讨论】:

        猜你喜欢
        • 2022-08-19
        • 2019-07-09
        • 2016-12-14
        • 2021-10-19
        • 2020-05-12
        • 2014-12-01
        • 2020-04-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多