以下是一个简单的示例,展示如何使用 Flutter 开发一个待办事项应用,并通过 FastAPI 和 SQLAlchemy 作为后端接口。为了简化代码,所有的 Flutter 前端代码将写在一个 main.dart 文件中。
1. 后端部分(FastAPI + SQLAlchemy)
首先,我们需要创建一个简单的 FastAPI 后端服务,用于处理待办事项的增删改查操作。
安装依赖
在你的 Python 环境中安装以下依赖:
pip install fastapi uvicorn sqlalchemy
创建 FastAPI 应用
创建一个文件 main.py,并写入以下代码:
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: str
class TodoItemUpdate(BaseModel):
title: str = None
completed: bool = None
# FastAPI 应用
app = FastAPI()
# 获取数据库会话
def get_db():
db = SessionLocal()
try:
yield db
finally:
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.title
if item.completed is not None:
db_item.completed = item.completed
db.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: flutter
http: ^0.13.5
provider: ^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 应用中添加待办事项,查看是否成功保存到后端。
- 勾选或取消勾选待办事项,查看是否更新状态。
- 长按待办事项删除,查看是否从后端删除。