快速开始示例
通过实际示例快速上手 flu-cli,创建一个简单的 Todo 应用。
目标
创建一个包含以下功能的 Todo 应用:
- ✅ 查看 Todo 列表
- ✅ 添加新 Todo
- ✅ 标记完成
- ✅ 删除 Todo
步骤 1: 创建项目
交互式创建
bash
flu-cli new按照提示输入:
- 项目名称:
todo_app - 模板: 选择
Modular - 项目路径: 回车(使用默认)
- 包名: 回车(使用默认
com.example.todo_app) - 作者: 输入你的名字
- 确认创建: 回车
或使用命令行
bash
flu-cli new todo_app -t modular
cd todo_app步骤 2: 创建 Todo 模块
bash
cd todo_app
flu-cli a module todo生成结构:
lib/features/todo/
├── pages/
├── viewmodels/
├── widgets/
├── services/
├── models/
└── index.dart步骤 3: 创建 Todo 模型
创建 JSON 文件
bash
cat > todo.json << 'EOF'
{
"id": "1",
"title": "Learn Flutter",
"description": "Complete Flutter tutorial",
"is_completed": false,
"created_at": "2024-01-01T00:00:00Z"
}
EOF生成模型
bash
flu-cli a model todo -f todo --json todo.json生成文件: lib/features/todo/models/todo_model.dart
查看生成的代码
dart
class Todo {
final String id;
final String title;
final String description;
final bool isCompleted;
final String createdAt;
Todo({
required this.id,
required this.title,
required this.description,
required this.isCompleted,
required this.createdAt,
});
factory Todo.fromJson(Map<String, dynamic> json) {
return Todo(
id: json['id'] as String,
title: json['title'] as String,
description: json['description'] as String,
isCompleted: json['is_completed'] as bool,
createdAt: json['created_at'] as String,
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'title': title,
'description': description,
'is_completed': isCompleted,
'created_at': createdAt,
};
}
}步骤 4: 创建 Todo 服务
bash
flu-cli a service todo -f todo --type storage生成文件: lib/features/todo/services/todo_service.dart
编辑服务
dart
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
import '../models/todo_model.dart';
class TodoService {
static const String _key = 'todos';
Future<List<Todo>> getTodos() async {
final prefs = await SharedPreferences.getInstance();
final String? todosJson = prefs.getString(_key);
if (todosJson == null) return [];
final List<dynamic> decoded = json.decode(todosJson);
return decoded.map((json) => Todo.fromJson(json)).toList();
}
Future<void> saveTodos(List<Todo> todos) async {
final prefs = await SharedPreferences.getInstance();
final String encoded = json.encode(
todos.map((todo) => todo.toJson()).toList(),
);
await prefs.setString(_key, encoded);
}
Future<void> addTodo(Todo todo) async {
final todos = await getTodos();
todos.add(todo);
await saveTodos(todos);
}
Future<void> updateTodo(Todo todo) async {
final todos = await getTodos();
final index = todos.indexWhere((t) => t.id == todo.id);
if (index != -1) {
todos[index] = todo;
await saveTodos(todos);
}
}
Future<void> deleteTodo(String id) async {
final todos = await getTodos();
todos.removeWhere((t) => t.id == id);
await saveTodos(todos);
}
}步骤 5: 创建 Todo 列表页面
bash
flu-cli a page todo_list -f todo生成文件:
lib/features/todo/pages/todo_list_page.dartlib/features/todo/viewmodels/todo_list_viewmodel.dart
编辑 ViewModel
dart
import 'package:flutter/foundation.dart';
import '../models/todo_model.dart';
import '../services/todo_service.dart';
class TodoListViewModel extends ChangeNotifier {
final TodoService _service = TodoService();
List<Todo> _todos = [];
List<Todo> get todos => _todos;
bool _isLoading = false;
bool get isLoading => _isLoading;
TodoListViewModel() {
loadTodos();
}
Future<void> loadTodos() async {
_isLoading = true;
notifyListeners();
try {
_todos = await _service.getTodos();
} catch (e) {
debugPrint('Error loading todos: $e');
} finally {
_isLoading = false;
notifyListeners();
}
}
Future<void> addTodo(String title, String description) async {
final todo = Todo(
id: DateTime.now().millisecondsSinceEpoch.toString(),
title: title,
description: description,
isCompleted: false,
createdAt: DateTime.now().toIso8601String(),
);
await _service.addTodo(todo);
await loadTodos();
}
Future<void> toggleTodo(Todo todo) async {
final updated = Todo(
id: todo.id,
title: todo.title,
description: todo.description,
isCompleted: !todo.isCompleted,
createdAt: todo.createdAt,
);
await _service.updateTodo(updated);
await loadTodos();
}
Future<void> deleteTodo(String id) async {
await _service.deleteTodo(id);
await loadTodos();
}
}编辑页面
dart
import 'package:flutter/material.dart';
import '../viewmodels/todo_list_viewmodel.dart';
class TodoListPage extends StatefulWidget {
const TodoListPage({super.key});
@override
State<TodoListPage> createState() => _TodoListPageState();
}
class _TodoListPageState extends State<TodoListPage> {
late final TodoListViewModel _viewModel;
final _titleController = TextEditingController();
final _descController = TextEditingController();
@override
void initState() {
super.initState();
_viewModel = TodoListViewModel();
_viewModel.addListener(() => setState(() {}));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Todo List'),
),
body: _viewModel.isLoading
? const Center(child: CircularProgressIndicator())
: _buildTodoList(),
floatingActionButton: FloatingActionButton(
onPressed: _showAddDialog,
child: const Icon(Icons.add),
),
);
}
Widget _buildTodoList() {
if (_viewModel.todos.isEmpty) {
return const Center(
child: Text('No todos yet. Add one!'),
);
}
return ListView.builder(
itemCount: _viewModel.todos.length,
itemBuilder: (context, index) {
final todo = _viewModel.todos[index];
return ListTile(
leading: Checkbox(
value: todo.isCompleted,
onChanged: (_) => _viewModel.toggleTodo(todo),
),
title: Text(
todo.title,
style: TextStyle(
decoration: todo.isCompleted
? TextDecoration.lineThrough
: null,
),
),
subtitle: Text(todo.description),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () => _viewModel.deleteTodo(todo.id),
),
);
},
);
}
void _showAddDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Add Todo'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: _titleController,
decoration: const InputDecoration(labelText: 'Title'),
),
TextField(
controller: _descController,
decoration: const InputDecoration(labelText: 'Description'),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
),
TextButton(
onPressed: () {
_viewModel.addTodo(
_titleController.text,
_descController.text,
);
_titleController.clear();
_descController.clear();
Navigator.pop(context);
},
child: const Text('Add'),
),
],
),
);
}
@override
void dispose() {
_titleController.dispose();
_descController.dispose();
_viewModel.dispose();
super.dispose();
}
}步骤 6: 添加依赖
编辑 pubspec.yaml:
yaml
dependencies:
flutter:
sdk: flutter
shared_preferences: ^2.2.0安装依赖:
bash
flutter pub get步骤 7: 更新路由
编辑 lib/core/router/app_router.dart:
dart
import 'package:flutter/material.dart';
import '../../features/todo/pages/todo_list_page.dart';
class AppRouter {
static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case '/':
return MaterialPageRoute(builder: (_) => const TodoListPage());
default:
return MaterialPageRoute(
builder: (_) => Scaffold(
body: Center(
child: Text('No route defined for ${settings.name}'),
),
),
);
}
}
}步骤 8: 运行项目
bash
flutter run完整项目结构
todo_app/
├── lib/
│ ├── main.dart
│ ├── app.dart
│ ├── core/
│ │ ├── router/
│ │ │ └── app_router.dart
│ │ └── theme/
│ │ └── app_theme.dart
│ └── features/
│ └── todo/
│ ├── models/
│ │ ├── todo_model.dart
│ │ └── index.dart
│ ├── services/
│ │ ├── todo_service.dart
│ │ └── index.dart
│ ├── viewmodels/
│ │ ├── todo_list_viewmodel.dart
│ │ └── index.dart
│ ├── pages/
│ │ ├── todo_list_page.dart
│ │ └── index.dart
│ └── index.dart
├── pubspec.yaml
└── README.md功能演示
1. 查看 Todo 列表
启动应用后,显示所有 Todo 项。
2. 添加 Todo
点击右下角的 + 按钮,输入标题和描述,点击 Add。
3. 标记完成
点击 Todo 项左侧的复选框,标记为完成/未完成。
4. 删除 Todo
点击 Todo 项右侧的删除图标,删除该项。
扩展功能
添加 Todo 详情页
bash
flu-cli a page todo_detail -f todo添加 Todo 编辑功能
bash
flu-cli a page todo_edit -f todo添加分类功能
bash
flu-cli a model category -f todo
flu-cli a service category -f todo --type storage添加搜索功能
在 ViewModel 中添加搜索方法:
dart
List<Todo> searchTodos(String query) {
return _todos.where((todo) =>
todo.title.toLowerCase().contains(query.toLowerCase()) ||
todo.description.toLowerCase().contains(query.toLowerCase())
).toList();
}总结
通过这个示例,你学会了:
- ✅ 使用 flu-cli 创建项目
- ✅ 创建功能模块
- ✅ 从 JSON 生成模型
- ✅ 创建服务层
- ✅ 创建页面和 ViewModel
- ✅ 实现完整的 CRUD 功能