Skip to content

FluCli 实战示例

概述

本文档将通过一个实际的示例,展示如何使用 FluCli 创建项目,并分别使用 base_normal_uibase_getx_ui 两种状态管理方案实现相同的功能。通过这个示例,您将了解两种方案的异同,以及如何根据项目需求选择合适的方案。

准备工作

安装 FluCli

首先,我们需要安装 FluCli 工具:

bash
# 使用 npm 全局安装
npm install -g flu-cli

# 或使用 yarn 全局安装
yarn global add flu-cli

# 或使用 pnpm 全局安装
pnpm add -g flu-cli

创建项目

使用 FluCli 创建一个新项目:

bash
# 启动交互式向导
flu-cli

按照向导提示,选择以下配置:

  1. 项目类型:应用 (app)
  2. 项目模板:pro (包含完整功能)
  3. 状态管理:根据需要选择 state 或 get
  4. 输入项目名称:flucli_example
  5. 输入包名:com.example.flucli_example
  6. 选择项目存放路径
  7. 选择要打开的 IDE

提示

如果您想同时体验两种状态管理方案,可以创建两个项目,分别选择 state 和 get 状态管理。

示例功能介绍

我们将实现一个简单的待办事项列表应用,包含以下功能:

  1. 显示待办事项列表
  2. 添加新的待办事项
  3. 标记待办事项为已完成
  4. 删除待办事项

使用 base_normal_ui 实现

创建数据模型

首先,我们需要创建一个待办事项的数据模型:

dart
// lib/models/todo_model.dart
class TodoModel {
  final int id;
  final String title;
  bool completed;

  TodoModel({
    required this.id,
    required this.title,
    this.completed = false,
  });

  TodoModel copyWith({
    int? id,
    String? title,
    bool? completed,
  }) {
    return TodoModel(
      id: id ?? this.id,
      title: title ?? this.title,
      completed: completed ?? this.completed,
    );
  }
}

创建待办事项列表页面

使用 base_normal_ui 创建待办事项列表页面:

dart
// lib/pages/todo_list_page.dart
import 'package:flutter/material.dart';
import 'package:flucli_example/common/base_ui/base_normal_ui/base_list_state.dart';
import 'package:flucli_example/models/todo_model.dart';

class TodoListPage extends StatefulWidget {
  const TodoListPage({Key? key}) : super(key: key);

  @override
  _TodoListPageState createState() => _TodoListPageState();
}

class _TodoListPageState extends BaseListState<TodoListPage> {
  final TextEditingController _textController = TextEditingController();

  @override
  void configDefault() {
    super.configDefault();
    appBarTitle = '待办事项';
    // 初始化一些示例数据
    dataList = [
      TodoModel(id: 1, title: '学习 Flutter'),
      TodoModel(id: 2, title: '学习 FluCli'),
      TodoModel(id: 3, title: '学习 base_normal_ui'),
    ];
    // 设置页面状态为成功
    pageState = PageState.successState;
  }

  @override
  Widget buildSuccessContent() {
    return Column(
      children: [
        _buildAddTodoInput(),
        Expanded(
          child: ListView.builder(
            itemCount: dataList.length,
            itemBuilder: (context, index) {
              return createListItem(index: index, dataList: dataList);
            },
          ),
        ),
      ],
    );
  }

  Widget _buildAddTodoInput() {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Row(
        children: [
          Expanded(
            child: TextField(
              controller: _textController,
              decoration: InputDecoration(
                hintText: '添加新的待办事项',
                border: OutlineInputBorder(),
              ),
            ),
          ),
          SizedBox(width: 16),
          ElevatedButton(
            onPressed: _addTodo,
            child: Text('添加'),
          ),
        ],
      ),
    );
  }

  void _addTodo() {
    if (_textController.text.isNotEmpty) {
      setState(() {
        dataList.add(TodoModel(
          id: dataList.length + 1,
          title: _textController.text,
        ));
        _textController.clear();
      });
    }
  }

  void _toggleTodo(int index) {
    setState(() {
      final todo = dataList[index] as TodoModel;
      dataList[index] = todo.copyWith(completed: !todo.completed);
    });
  }

  void _deleteTodo(int index) {
    setState(() {
      dataList.removeAt(index);
    });
  }

  @override
  Widget createListItem({required int index, required List dataList}) {
    final todo = dataList[index] as TodoModel;
    return ListTile(
      leading: Checkbox(
        value: todo.completed,
        onChanged: (_) => _toggleTodo(index),
      ),
      title: Text(
        todo.title,
        style: TextStyle(
          decoration: todo.completed ? TextDecoration.lineThrough : null,
        ),
      ),
      trailing: IconButton(
        icon: Icon(Icons.delete),
        onPressed: () => _deleteTodo(index),
      ),
    );
  }

  @override
  Future<void> getNetWorkData({required int type}) async {
    // 在实际应用中,这里会从网络获取数据
    // 本示例中使用本地数据,所以这个方法不需要实现
  }

  @override
  void dispose() {
    _textController.dispose();
    super.dispose();
  }
}

注册路由

在路由配置中添加待办事项列表页面:

dart
// lib/config/router/routes.dart
import 'package:flucli_example/pages/todo_list_page.dart';

class Routes {
  static final routes = {
    // 其他路由...
    '/todo_list': (context) => const TodoListPage(),
  };
}

更新主页面

在主页面添加一个按钮,用于导航到待办事项列表页面:

dart
// 在主页面的某个位置添加
ElevatedButton(
  onPressed: () {
    Navigator.pushNamed(context, '/todo_list');
  },
  child: Text('待办事项列表'),
),

使用 base_getx_ui 实现

创建数据模型

数据模型与 base_normal_ui 实现相同:

dart
// lib/models/todo_model.dart
class TodoModel {
  final int id;
  final String title;
  bool completed;

  TodoModel({
    required this.id,
    required this.title,
    this.completed = false,
  });

  TodoModel copyWith({
    int? id,
    String? title,
    bool? completed,
  }) {
    return TodoModel(
      id: id ?? this.id,
      title: title ?? this.title,
      completed: completed ?? this.completed,
    );
  }
}

创建控制器

使用 GetX 创建控制器:

dart
// lib/vm/todo_list_controller.dart
import 'package:get/get.dart';
import 'package:flucli_example/common/base_ui/base_getx_ui/base_getx_list_controller.dart';
import 'package:flucli_example/models/todo_model.dart';

class TodoListController extends BaseGetXListController {
  final textController = TextEditingController();

  @override
  void onInit() {
    super.onInit();
    // 初始化一些示例数据
    dataList.value = [
      TodoModel(id: 1, title: '学习 Flutter'),
      TodoModel(id: 2, title: '学习 FluCli'),
      TodoModel(id: 3, title: '学习 base_getx_ui'),
    ];
    // 设置页面状态为成功
    pageState.value = PageState.successState;
  }

  void addTodo() {
    if (textController.text.isNotEmpty) {
      dataList.add(TodoModel(
        id: dataList.length + 1,
        title: textController.text,
      ));
      textController.clear();
    }
  }

  void toggleTodo(int index) {
    final todo = dataList[index] as TodoModel;
    dataList[index] = todo.copyWith(completed: !todo.completed);
  }

  void deleteTodo(int index) {
    dataList.removeAt(index);
  }

  @override
  Future<void> getNetWorkData({required int type}) async {
    // 在实际应用中,这里会从网络获取数据
    // 本示例中使用本地数据,所以这个方法不需要实现
  }

  @override
  void onClose() {
    textController.dispose();
    super.onClose();
  }
}

创建待办事项列表页面

使用 base_getx_ui 创建待办事项列表页面:

dart
// lib/pages/todo_list_page.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flucli_example/common/base_ui/base_getx_ui/base_getx_list_v.dart';
import 'package:flucli_example/models/todo_model.dart';
import 'package:flucli_example/vm/todo_list_controller.dart';

class TodoListPage extends BaseGetXListV<TodoListController> {
  const TodoListPage({Key? key}) : super(key: key);

  @override
  TodoListController get controller => Get.put(TodoListController());

  @override
  void configDefault() {
    super.configDefault();
    appBarTitle = '待办事项';
  }

  @override
  Widget buildSuccessContent() {
    return Column(
      children: [
        _buildAddTodoInput(),
        Expanded(
          child: Obx(() {
            return ListView.builder(
              itemCount: controller.dataList.length,
              itemBuilder: (context, index) {
                return createListItem(
                  index: index,
                  dataList: controller.dataList,
                );
              },
            );
          }),
        ),
      ],
    );
  }

  Widget _buildAddTodoInput() {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Row(
        children: [
          Expanded(
            child: TextField(
              controller: controller.textController,
              decoration: InputDecoration(
                hintText: '添加新的待办事项',
                border: OutlineInputBorder(),
              ),
            ),
          ),
          SizedBox(width: 16),
          ElevatedButton(
            onPressed: controller.addTodo,
            child: Text('添加'),
          ),
        ],
      ),
    );
  }

  @override
  Widget createListItem({required int index, required List dataList}) {
    final todo = dataList[index] as TodoModel;
    return ListTile(
      leading: Checkbox(
        value: todo.completed,
        onChanged: (_) => controller.toggleTodo(index),
      ),
      title: Text(
        todo.title,
        style: TextStyle(
          decoration: todo.completed ? TextDecoration.lineThrough : null,
        ),
      ),
      trailing: IconButton(
        icon: Icon(Icons.delete),
        onPressed: () => controller.deleteTodo(index),
      ),
    );
  }
}

注册路由

在 GetX 中注册路由:

dart
// lib/config/router/routes.dart
import 'package:get/get.dart';
import 'package:flucli_example/pages/todo_list_page.dart';

class AppRoutes {
  static final routes = [
    // 其他路由...
    GetPage(
      name: '/todo_list',
      page: () => const TodoListPage(),
    ),
  ];
}

更新主页面

在主页面添加一个按钮,用于导航到待办事项列表页面:

dart
// 在主页面的某个位置添加
ElevatedButton(
  onPressed: () {
    Get.toNamed('/todo_list');
  },
  child: Text('待办事项列表'),
),

两种方案的对比

代码结构对比

方面base_normal_uibase_getx_ui
状态管理使用 Flutter 原生的 setState使用 GetX 的响应式变量
代码分离UI 和逻辑在同一个类中UI 和逻辑分离到不同的类
依赖注入手动管理依赖使用 Get.put 自动管理依赖
路由管理使用 Navigator使用 Get.toNamed
响应式更新需要手动调用 setState自动响应数据变化

优缺点分析

base_normal_ui

优点:

  • 学习成本低,使用 Flutter 原生 API
  • 不依赖第三方库,减少包大小
  • 代码逻辑直观,易于理解

缺点:

  • UI 和逻辑耦合度高
  • 状态管理较为繁琐,需要手动调用 setState
  • 大型应用中可能导致代码臃肿

base_getx_ui

优点:

  • UI 和逻辑分离,代码结构清晰
  • 响应式编程,自动更新 UI
  • 内置依赖注入和路由管理
  • 适合大型应用开发

缺点:

  • 学习成本较高,需要了解 GetX 框架
  • 依赖第三方库,增加包大小
  • 对 Flutter 原理理解要求较高

如何选择

  1. 项目规模:小型项目可以选择 base_normal_ui,大型项目推荐 base_getx_ui
  2. 团队经验:Flutter 新手推荐 base_normal_ui,有经验的开发者可以选择 base_getx_ui
  3. 性能要求:对性能要求极高的场景,base_normal_ui 可能更有优势
  4. 开发效率:追求开发效率,base_getx_ui 更胜一筹

总结

通过本示例,我们展示了如何使用 FluCli 创建项目,并分别使用 base_normal_uibase_getx_ui 两种状态管理方案实现相同的功能。两种方案各有优缺点,您可以根据项目需求和团队情况选择合适的方案。

无论选择哪种方案,FluCli 都提供了完善的项目结构和基础组件,帮助您快速开发高质量的 Flutter 应用。

提示

想要了解更多关于 FluCli 的信息,请查看 FluCli 官方文档

根据 MIT 许可发布。