【问题标题】:Separating UI and Logic in Flutter在 Flutter 中分离 UI 和逻辑
【发布时间】:2021-07-10 13:53:09
【问题描述】:

通常,我使用一个单独的类,并在小部件顶部声明一个对象。我想知道那个架构有什么问题。

我在Flutter中遇到了一个完整的包,WidgetView,它需要声明一个依赖,然后做一个状态对象,然后做同样的事情。

为什么不只是一个简单的类来实现相同的目标。如下所示

class NewAccountComponent extends StatelessWidget {  
final NewAccountComponentLogic logic = NewAccountComponentLogic();
  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: Text('Enter a Unique Account Number'),
      titlePadding: EdgeInsets.all(20.0),
      content: TextFormField(
        controller: logic.controller,
            onPressed: () => logic.clearTextFormField(),
          ),
        ),
}
class NewAccountComponentLogic {
  static String accountNumber;
  static bool existsAccountNumber;
  TextEditingController controller = TextEditingController();
  clearTextFormField() {
    controller.text = '';
    accountNumber = '';
}

【问题讨论】:

    标签: flutter user-interface widget logic


    【解决方案1】:

    我稍微修改了您的代码。如果您像以下代码一样更改代码,希望您会得到预期的输出。

    class NewAccountComponent extends StatelessWidget {
      final NewAccountComponentLogic logic = NewAccountComponentLogic(
        '123456',
        true,
        TextEditingController(),
      );
      @override
      Widget build(BuildContext context) {
        return AlertDialog(
          title: Text('Enter a Unique Account Number'),
          titlePadding: EdgeInsets.all(20.0),
          content: TextFormField(
            controller: logic.controller,
            
          ),
          actions: <Widget>[
              TextButton(
                child: Text('Done'),
                onPressed: () {
                  print(logic.controller.text);
                  logic.clearTextFormField();
                },
              ),
            ],
        );
      }
    }
    
    class NewAccountComponentLogic {
      String accountNumber;
      bool existsAccountNumber;
      TextEditingController controller;
      NewAccountComponentLogic(
        this.accountNumber,
        this.existsAccountNumber,
        this.controller,
      );
    
      void clearTextFormField() {
        controller.text = '';
        accountNumber = '';
      }
    

    @Ignacior 还提供了一个不错的解决方案,您可以遵循。

    【讨论】:

      【解决方案2】:

      您可以通过多种方式分离小部件逻辑和表示。我见过的(你提到的)是使用 WidgetView 模式。你可以在没有任何依赖的情况下做到这一点:

      1. 创建一个抽象类,其中包含所有 WidgetView 应实现的逻辑:

      对于无状态小部件:

      abstract class StatelessView<T1> extends StatelessWidget {
        final T1 widget;
        const StatelessView(this.widget, {Key key}) : super(key: key);
        
        @override
        Widget build(BuildContext context);
      }
      

      对于有状态的小部件:

      abstract class WidgetView<T1, T2> extends StatelessWidget {
        final T2 state;
        T1 get widget => (state as State).widget as T1;
      
        const WidgetView(this.state, {Key key}) : super(key: key);
        
        @override
        Widget build(BuildContext context);
      }
      
      1. 正常创建小部件:
      // Note it's a StatefulWidget because accountNumber mutates
      class NewAccountComponent extends StatefulWidget {
        @override
        _NewAccountComponentState createState() => _NewAccountComponentState();
      }
      
      class _NewAccountComponentState extends State<NewAccountComponent> {
        String accountNumber;
        bool existsAccountNumber;
        final TextEditingController controller = TextEditingController();
      
        clearTextFormField() {
          controller.text = '';
          accountNumber = '';
        }
      
        @override
        Widget build(BuildContext context) {
          return AlertDialog(
            title: Text('Enter a Unique Account Number'),
            titlePadding: EdgeInsets.all(20.0),
            content: TextFormField(
              controller: controller,
              onSaved: (value) => clearTextFormField(),
            ),
          );
        }
      }
      
      1. 如果小部件是Stateful
      class NewAccountComponent extends StatefulWidget {
        @override
        _NewAccountComponentController createState() => _NewAccountComponentController();
      }
      
      // State suffix renamed to Controller
      // This class has all widget logic
      class _NewAccountComponentController extends State<NewAccountComponent> {
        String accountNumber;
        bool existsAccountNumber;
        final TextEditingController controller = TextEditingController();
      
        clearTextFormField() {
          controller.text = '';
          accountNumber = '';
        }
      
        // In build, returns a new instance of your view, sending the current state
        @override
        Widget build(BuildContext context) => _NewAccountComponentView(this);
      }
      
      // View extends of WidgetView and has a current state to access widget logic
      // with widget you can access to StatefulWidget parent
      class _NewAccountComponentView
          extends WidgetView<NewAccountComponent, _NewAccountComponentController> {
      
        _NewAccountComponentView(_NewAccountComponentController state): super(state);
      
        @override
        Widget build(BuildContext context) {
          return AlertDialog(
            title: Text('Enter a Unique Account Number'),
            titlePadding: EdgeInsets.all(20.0),
            content: TextFormField(
              controller: state.controller,
              onSaved: (value) => state.clearTextFormField(),
            ),
          );
        }
      }
      
      1. 如果它是无状态的,则更改为:
      class MyStatelessWidget extends StatelessWidget {
        final String textContent = "Hello!";
      
        const MyStatelessWidget({Key key}) : super(key: key);
      
        @override
        Widget build(BuildContext context) {
          return Container(
            child: Text(textContent),
          );
        }
      }
      

      到:

      // Widget and logic controller are unit
      class MyStatelessWidget extends StatelessWidget {
        final String textContent = "Hello!";
      
        const MyStatelessWidget({Key key}) : super(key: key);
      
        @override
        Widget build(BuildContext context) => _MyStatelessView(this);
      }
      
      // The view is separately
      class _MyStatelessView extends StatelessView<MyStatelessWidget> {
        _MyStatelessView(MyStatelessWidget widget) : super(widget);
      
        @override
        Widget build(BuildContext context) {
          return Container(
            child: Text(widget.textContent),
          );
        }
      }
      

      参考资料:

      Flutter: WidgetView — A Simple Separation of Layout and Logic

      【讨论】:

      • 感谢您的详细解释。但是为什么不是一个简单的类,可以完成这项工作...为什么我还需要为逻辑扩展无状态小部件。
      • 当然,将其分离为包含所有逻辑的单个类的想法是有效的。我只是展示了一种方法并遵循模式的规则:每个状态(或 StatelessWidget)都有一个子 WidgetView,其中包含声明性视图代码。 State 充当 WidgetView 的替代 Controller / Mediator / Presenter,响应视图事件并提供对状态的访问。 WidgetView 只是一个纯布局的 StatelessWidget。
      • 您可以采用更适合问题的方法。对于逻辑很少的小部件,我认为没有必要分成多个部分,更不用说应用我提到的模式了。
      • 非常感谢。我现在很清楚这个问题。
      猜你喜欢
      • 1970-01-01
      • 2011-04-10
      • 2022-10-03
      • 2022-11-23
      • 2018-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多