以下是一个简单的示例,展示如何使用 Flutter 开发一个待办事项应用,并通过 FastAPI 和 SQLAlchemy 作为后端接口。为了简化代码,所有的 Flutter 前端代码将写在一个 main.dart 文件中。
1. 后端部分(FastAPI + SQLAlchemy)
首先,我们需要创建一个简单的 FastAPI 后端服务,用于处理待办事项的增删改查操作。
安装依赖
在你的 Python 环境中安装以下依赖:
pip install fastapi uvicorn sqlalchemy
创建 FastAPI 应用
创建一个文件 main.py,并写入以下代码:
python">from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from sqlalchemy import create_engine, Column, Integer, String, Boolean
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker# 数据库配置
DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()# 待办事项模型
class TodoItem(Base):__tablename__ = "todo_items"id = Column(Integer, primary_key=True, index=True)title = Column(String, index=True)completed = Column(Boolean, default=False)# 创建数据库表
Base.metadata.create_all(bind=engine)# Pydantic 模型
class TodoItemCreate(BaseModel):title: strclass TodoItemUpdate(BaseModel):title: str = Nonecompleted: bool = None# FastAPI 应用
app = FastAPI()# 获取数据库会话
def get_db():db = SessionLocal()try:yield dbfinally:db.close()# 获取所有待办事项
@app.get("/todos/")
def read_todos():db = SessionLocal()items = db.query(TodoItem).all()return [{"id": item.id, "title": item.title, "completed": item.completed} for item in items]# 创建待办事项
@app.post("/todos/")
def create_todo(item: TodoItemCreate):db = SessionLocal()new_item = TodoItem(title=item.title, completed=False)db.add(new_item)db.commit()db.refresh(new_item)return {"id": new_item.id, "title": new_item.title, "completed": new_item.completed}# 更新待办事项
@app.put("/todos/{todo_id}")
def update_todo(todo_id: int, item: TodoItemUpdate):db = SessionLocal()db_item = db.query(TodoItem).filter(TodoItem.id == todo_id).first()if not db_item:raise HTTPException(status_code=404, detail="Item not found")if item.title is not None:db_item.title = item.titleif item.completed is not None:db_item.completed = item.completeddb.commit()db.refresh(db_item)return {"id": db_item.id, "title": db_item.title, "completed": db_item.completed}# 删除待办事项
@app.delete("/todos/{todo_id}")
def delete_todo(todo_id: int):db = SessionLocal()db_item = db.query(TodoItem).filter(TodoItem.id == todo_id).first()if not db_item:raise HTTPException(status_code=404, detail="Item not found")db.delete(db_item)db.commit()return {"message": "Item deleted"}
启动 FastAPI 服务
运行以下命令启动服务:
uvicorn main:app --reload
服务默认运行在 http://127.0.0.1:8000,你可以通过访问 /docs 查看 API 文档。
2. Flutter 前端部分(main.dart)
接下来,我们编写 Flutter 应用来与后端交互。所有代码将写在一个 main.dart 文件中。
安装依赖
在 Flutter 项目的 pubspec.yaml 文件中添加以下依赖:
dependencies:flutter:sdk: flutterhttp: ^0.13.5provider: ^6.0.5
运行 flutter pub get 安装依赖。
编写 Flutter 代码
以下是完整的 main.dart 文件代码:
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;void main() {runApp(MyApp());
}class MyApp extends StatelessWidget {Widget build(BuildContext context) {return MaterialApp(title: 'Todo App',theme: ThemeData(primarySwatch: Colors.blue,),home: TodoListScreen(),);}
}class TodoListScreen extends StatefulWidget { _TodoListScreenState createState() => _TodoListScreenState();
}class _TodoListScreenState extends State<TodoListScreen> {List<dynamic> _todos = [];final TextEditingController _textEditingController = TextEditingController();void initState() {super.initState();_fetchTodos();}Future<void> _fetchTodos() async {try {final response = await http.get(Uri.parse('http://127.0.0.1:8000/todos/'));if (response.statusCode == 200) {setState(() {_todos = json.decode(response.body);});} else {throw Exception('Failed to load todos');}} catch (e) {print('Error fetching todos: $e');}}Future<void> _createTodo(String title) async {try {final response = await http.post(Uri.parse('http://127.0.0.1:8000/todos/'),headers: {'Content-Type': 'application/json'},body: json.encode({'title': title}),);if (response.statusCode == 200) {_fetchTodos();} else {throw Exception('Failed to create todo');}} catch (e) {print('Error creating todo: $e');}}Future<void> _updateTodo(int id, {String? title, bool? completed}) async {try {final response = await http.put(Uri.parse('http://127.0.0.1:8000/todos/$id'),headers: {'Content-Type': 'application/json'},body: json.encode({'title': title,'completed': completed,}),);if (response.statusCode == 200) {_fetchTodos();} else {throw Exception('Failed to update todo');}} catch (e) {print('Error updating todo: $e');}}Future<void> _deleteTodo(int id) async {try {final response = await http.delete(Uri.parse('http://127.0.0.1:8000/todos/$id'));if (response.statusCode == 200) {_fetchTodos();} else {throw Exception('Failed to delete todo');}} catch (e) {print('Error deleting todo: $e');}}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('Todo List'),),body: Column(children: [Expanded(child: ListView.builder(itemCount: _todos.length,itemBuilder: (context, index) {final todo = _todos[index];return ListTile(title: Text(todo['title']),trailing: Checkbox(value: todo['completed'],onChanged: (bool? value) {_updateTodo(todo['id'], completed: value);},),onLongPress: () {_deleteTodo(todo['id']);},);},),),Padding(padding: const EdgeInsets.all(8.0),child: Row(children: [Expanded(child: TextField(controller: _textEditingController,decoration: InputDecoration(hintText: 'Enter a new todo',),),),IconButton(icon: Icon(Icons.add),onPressed: () {final title = _textEditingController.text;if (title.isNotEmpty) {_createTodo(title);_textEditingController.clear();}},),],),),],),);}
}
3. 运行和测试
启动 FastAPI 后端服务:
uvicorn main:app --reload
运行 Flutter 应用:
在 Flutter 项目目录下运行:
flutter run
测试功能:
- 在 Flutter 应用中添加待办事项,查看是否成功保存到后端。
- 勾选或取消勾选待办事项,查看是否更新状态。
- 长按待办事项删除,查看是否从后端删除。