Flutter MVC架构插件flutter_mvc的使用

Flutter MVC架构插件flutter_mvc的使用

在本教程中,我们将介绍如何使用flutter_mvc插件来实现Flutter应用中的MVC(Model-View-Controller)架构。通过以下步骤,您可以快速上手并掌握flutter_mvc的基本用法。

快速开始

使用MVC

首先,我们需要创建modelviewcontroller类。

// 定义Model类
class HomeModel {
  const HomeModel(this.title);
  final String title;
}

// 定义Controller类
class HomeController extends MvcController<HomeModel> {
  [@override](/user/override)
  MvcView<HomeController> view() => HomeView();
}

// 定义View类
class HomeView extends MvcView<HomeController> {
  [@override](/user/override)
  Widget buildView() {
    return Center(
      child: Text(controller.model.title),
    );
  }
}

然后,我们可以在Flutter应用中使用Mvc组件来实例化和展示这些类。

// 在Flutter应用中使用Mvc
Mvc(
  create: () => HomeController(),
  model: const HomeModel('Flutter Mvc Demo'),
)

这将显示文本Flutter Mvc Demo

更新MVC

如果您需要更新Model,可以这样操作:

class _MyHomePageState extends State<MyHomePage> {
  String title = 'Flutter Mvc Demo';

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: Mvc(
        create: () => HomeController(),
        model: HomeModel(title),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            title = 'Flutter Mvc Demo Updated';
          });
        },
        tooltip: 'Update Title',
        child: const Icon(Icons.add),
      ),
    );
  }
}

点击按钮后,HomeModeltitle将被更新,并且HomeView也会随之更新。

控制器生命周期

Controller的生命周期与StatefulWidget中的State生命周期一致。您可以通过重写一些方法来处理这些生命周期事件:

class HomeController extends MvcController<HomeModel> {
  [@override](/user/override)
  void init() {
    super.init();
  }

  [@override](/user/override)
  void didUpdateModel(HomeModel oldModel) {
    super.didUpdateModel(oldModel);
  }

  [@override](/user/override)
  void activate() {
    super.activate();
  }

  [@override](/user/override)
  void deactivate() {
    super.deactivate();
  }

  [@override](/user/override)
  void dispose() {
    super.dispose();
  }

  [@override](/user/override)
  MvcView<MvcController> view() => HomeView();
}

更新Widget

更新MvcView

通过调用update()方法,您可以更新整个MvcView

class HomeController extends MvcController {
  String title = "Default Title";

  void tapUpdate() {
    title = "Title Updated";
    update();
  }

  [@override](/user/override)
  MvcView<MvcController> view() => HomeView();
}

class HomeView extends MvcView<HomeController> {
  [@override](/user/override)
  Widget buildView() {
    return Scaffold(
      body: Center(
        child: Text(controller.title),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: controller.tapUpdate,
        tooltip: 'Update Title',
        child: const Icon(Icons.add),
      ),
    );
  }
}

使用Widget类型更新特定Widget

class HomeController extends MvcController {
  String title = "Default Title";
  String body = "Default Body";

  void tapUpdate() {
    title = "Title Updated";
    body = "Body Updated";
    querySelectorAll<MvcHeader>().update(); // 更新所有MvcHeader,或者使用querySelectorAll("MvcHeader").update();
    querySelectorAll("MvcBody,MvcHeader").update(); // 更新所有MvcBody和MvcHeader
  }

  [@override](/user/override)
  MvcView<MvcController> view() => HomeView();
}

class HomeView extends MvcView<HomeController> {
  [@override](/user/override)
  Widget buildView() {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          MvcHeader(
            builder: (_) => Text(controller.title),
          ),
          MvcBody(
            builder: (_) => Text(controller.body),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: controller.tapUpdate,
        tooltip: 'Update',
        child: const Icon(Icons.add),
      ),
    );
  }
}

只有继承自MvcStatelessWidgetMvcStatefulWidget的Widget才能使用这种方式更新。

使用id、类、属性更新特定Widget

class HomeController extends MvcController {
  String title = "Default Title";

  void tapUpdateById() {
    querySelectorAll('#title_id').update(() => title = "Title Updated By Id");
  }

  void tapUpdateByClass() {
    querySelectorAll('.title_class').update(() => title = "Title Updated By Class");
  }
  void tapUpdateByAttribute() {
    querySelectorAll('[data-title]').update(() => title = "Title Updated By Attribute");
  }

  [@override](/user/override)
  MvcView<MvcController> view() => HomeView();
}

class HomeView extends MvcView<HomeController> {
  [@override](/user/override)
  Widget buildView() {
    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          MvcBuilder(
            id: "title_id",
            classes: const ["title_class"],
            attributes: const {"data-title": "title"},
            builder: (_) {
              return Text(controller.title);
            },
          ),
          TextButton(
            onPressed: controller.tapUpdateById,
            child: const Text("Update By Id"),
          ),
          TextButton(
            onPressed: controller.tapUpdateByClass,
            child: const Text("Update By Class"),
          ),
          TextButton(
            onPressed: controller.tapUpdateByAttribute,
            child: const Text("Update By Attribute"),
          ),
        ],
      ),
    );
  }
}

querySelectorAll遵循W3C选择器规则。它可用于同时更新符合规则的多个Widget,但不支持兄弟选择器。

依赖注入

依赖注入是flutter_mvc的核心功能之一。您只需提供对象类型及其创建方法,框架会在需要时自动创建并提供该对象。它提供了三种不同的生命周期模式:单例模式、瞬态模式和范围模式。

概述

  • 单例模式:对象仅创建一次,后续获取时返回相同的对象。
  • 瞬态模式:每次获取时都会创建一个新对象。
  • 范围模式:每次获取时都会创建一个新对象,但在同一范围内获取的对象是相同的。

flutter_mvc中,每个从MvcStatefulWidgetMvcStatelessWidget扩展的Widget都是一个新的范围,包括MvcMvcBuilderMvcHeaderMvcBody等。

MvcDependencyProvider(
  provider: (collection) {
    collection.addSingleton<TestService1>((_) => TestService1());
    collection.add<TestService2>((_) => TestService2());
    collection.addScopedSingleton<TestService3>((serviceProvider) => TestService3());
  },
  child: Mvc(create: () => HomeController()),
)

获取对象

当获取对象时,您可以获取当前范围及其所有父范围中注入的对象。

class HomeController extends MvcController {
  [@override](/user/override)
  void init() {
    super.init();
    final TestService1 service1 = getService<TestService1>();
    final TestService2 service2 = getService<TestService2>();
    final TestService3 service3 = getService<TestService3>();
  }

  [@override](/user/override)
  MvcView<MvcController> view() => HomeView();
}

class HomeView extends MvcView<HomeController> {
  [@override](/user/override)
  Widget buildView() {
    final TestService1 service1 = getService<TestService1>();
    final TestService2 service2 = getService<TestService2>();
    final TestService3 service3 = getService<TestService3>();

    return Scaffold(
      body: Center(
        child: MvcBuilder(
          builder: (context) {
            final TestService1 service1 = context.getService<TestService1>();
            final TestService2 service2 = context.getService<TestService2>();
            final TestService3 service3 = context.getService<TestService3>();
            return const Text("Hello, World!");
          },
        ),
      ),
    );
  }
}

使用依赖注入对象更新Widget

如果注入的对象实现了MvcService接口,您可以使用一些方法来更新Widget。

class TestService with DependencyInjectionService, MvcService {
  String title = "title";
  void test() {
    update(() => title = "new title");
  }
}

MvcDependencyProvider(
  provider: (collection) {
    collection.addSingleton<TestService>((_) => TestService());
  },
  child: Scaffold(
    body: MvcServiceScope<TestService>(
      builder: (MvcContext context, TestService service) {
        return Text(service.title);
      },
    ),
    floatingActionButton: Builder(
      builder: (context) {
        return FloatingActionButton(
          onPressed: () {
            context.getMvcService<TestService>().test();
          },
          child: const Icon(Icons.add),
        );
      },
    ),
  ),
)

点击按钮将更新Text的内容。

完整示例Demo

import 'dart:async';
import 'dart:math';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mvc/flutter_mvc.dart';

void main() {
  runApp(
    MvcDependencyProvider(
      provider: (collection) {
        collection.addSingleton<TestService>((_) => TestService());
      },
      child: const MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: Mvc(
        create: () => TestMvcController(),
        model: const TestModel("Flutter Mvc Demo"),
      ),
    );
  }
}

/// The dependency injection service
class TestService with DependencyInjectionService, MvcService {
  String title = "Default Title";

  void changeTitle() {
    title = "Service Changed Title";
    update();
  }
}

/// The Model
class TestModel {
  const TestModel(this.title);
  final String title;
}

/// The Controller
class TestMvcController extends MvcController<TestModel> {
  int count = 0;
  int timerCount = 0;
  late Timer timer;

  [@override](/user/override)
  void init() {
    super.init();
    timer = Timer.periodic(const Duration(seconds: 1), timerCallback);
  }

  [@override](/user/override)
  MvcView view() => TestMvcView();

  [@override](/user/override)
  void dispose() {
    super.dispose();
    timer.cancel();
  }

  /// timer callback
  void timerCallback(Timer timer) {
    // update the widget with classes "timerCount"
    querySelectorAll(".timerCount").update(() => timerCount++);
  }

  /// click the FloatingActionButton
  void tapAdd() {
    count++;
    // update the widget with id "count"
    querySelectorAll("#count").update();
  }

  /// click the "update title by controller"
  void changeTestServiceTitle() {
    // get TestService and set title
    getService<TestService>().title = "Controller Changed Title";
    // update The TestService, will be update all MvcServiceScope<TestService>
    getService<TestService>().update();
  }
}

/// The View
class TestMvcView extends MvcView<TestMvcController> {
  [@override](/user/override)
  Widget buildView() {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(controller.model.title),
      ),
      body: Column(
        children: [
          MvcHeader(
            builder: (context) {
              return Container(
                height: 44,
                color: Color(
                  Random().nextInt(0xffffffff),
                ),
              );
            },
          ),
          Expanded(
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  MvcServiceScope<TestService>(
                    builder: (context, service) {
                      return Text(service.title);
                    },
                  ),
                  MvcBuilder(
                    classes: const ["timerCount"],
                    builder: (context) {
                      return Text(
                        '${controller.timerCount}',
                        style: Theme.of(context).textTheme.headlineMedium,
                      );
                    },
                  ),
                  MvcBuilder(
                    id: "count",
                    builder: (context) {
                      return Text(
                        '${controller.count}',
                        style: Theme.of(context).textTheme.headlineMedium,
                      );
                    },
                  ),
                  CupertinoButton(
                    onPressed: controller.changeTestServiceTitle,
                    child: const Text("update title by controller"),
                  ),
                  CupertinoButton(
                    onPressed: () => getService<TestService>().changeTitle(),
                    child: const Text("update title by self service"),
                  ),
                  CupertinoButton(
                    onPressed: () => controller.querySelectorAll<MvcHeader>().update(),
                    child: const Text("update header"),
                  ),
                  CupertinoButton(
                    onPressed: () => controller.querySelectorAll<MvcFooter>().update(),
                    child: const Text("update footer"),
                  ),
                ],
              ),
            ),
          ),
          MvcFooter(
            builder: (context) {
              return Container(
                height: 44,
                color: Color(
                  Random().nextInt(0xffffffff),
                ),
              );
            },
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: controller.tapAdd,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

更多关于Flutter MVC架构插件flutter_mvc的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter MVC架构插件flutter_mvc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


flutter_mvc 是一个基于 Flutter 的 MVC(Model-View-Controller)架构插件,它帮助开发者更好地组织和管理 Flutter 应用中的代码。通过将应用程序的逻辑、数据和界面分离,flutter_mvc 使得代码更加模块化、易于维护和扩展。

安装 flutter_mvc 插件

首先,你需要在 pubspec.yaml 文件中添加 flutter_mvc 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_mvc: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来安装依赖。

使用 flutter_mvc 插件

1. 创建 Model

Model 负责管理应用程序的数据和业务逻辑。你可以创建一个继承自 MvcModel 的类。

import 'package:flutter_mvc/flutter_mvc.dart';

class CounterModel extends MvcModel {
  int count = 0;

  void increment() {
    count++;
    notifyListeners(); // 通知 View 更新
  }
}

2. 创建 Controller

Controller 负责处理用户交互和业务逻辑。你可以创建一个继承自 MvcController 的类,并在其中引用 Model。

import 'package:flutter_mvc/flutter_mvc.dart';

class CounterController extends MvcController<CounterModel> {
  CounterController() : super(CounterModel());

  void incrementCounter() {
    model.increment();
  }
}

3. 创建 View

View 负责展示数据和处理用户交互。你可以使用 MvcView 来绑定 Controller 和 Model。

import 'package:flutter/material.dart';
import 'package:flutter_mvc/flutter_mvc.dart';

class CounterView extends MvcView<CounterController, CounterModel> {
  CounterView() : super(CounterController());

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Counter App'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Count:',
            ),
            Text(
              '${model.count}',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: controller.incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

4. 运行应用

最后,在你的 main.dart 文件中运行应用:

import 'package:flutter/material.dart';
import 'counter_view.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter MVC Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: CounterView(),
    );
  }
}
回到顶部