Flutter 简单教程
概述
Flutter是Google开发的跨平台移动应用开发框架,使用Dart语言编写。本教程将从UI设计和代码逻辑两个方面介绍Flutter应用开发。
一、UI开发方面
1. Widget基础
Flutter应用由Widget组成,Widget是Flutter的基本构建块。
常用Widget类型
// 基础Widget
Text('Hello World')
Image.asset('assets/image.png')
Icon(Icons.star)// 布局Widget
Container(width: 100,height: 100,color: Colors.blue,
)Column(children: [Text('Item 1'),Text('Item 2'),],
)Row(children: [Icon(Icons.star),Text('Rating'),],
)
2. 页面布局
Material Design风格
import 'package:flutter/material.dart';class MyHomePage extends StatelessWidget {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('我的应用'),backgroundColor: Colors.blue,),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Text('欢迎使用Flutter!',style: TextStyle(fontSize: 24),),SizedBox(height: 20),ElevatedButton(onPressed: () {// 按钮点击事件},child: Text('点击我'),),],),),floatingActionButton: FloatingActionButton(onPressed: () {// FAB点击事件},child: Icon(Icons.add),),);}
}
Cupertino风格(iOS风格)
import 'package:flutter/cupertino.dart';class IosStylePage extends StatelessWidget {@overrideWidget build(BuildContext context) {return CupertinoPageScaffold(navigationBar: CupertinoNavigationBar(middle: Text('iOS风格页面'),),child: Center(child: CupertinoButton(onPressed: () {},child: Text('iOS风格按钮'),),),);}
}
3. 响应式设计
媒体查询
class ResponsiveLayout extends StatelessWidget {@overrideWidget build(BuildContext context) {// 获取屏幕尺寸final screenWidth = MediaQuery.of(context).size.width;final screenHeight = MediaQuery.of(context).size.height;return Container(width: screenWidth > 600 ? 300 : 200, // 根据屏幕宽度调整height: screenHeight * 0.5, // 屏幕高度的50%child: Text('响应式布局'),);}
}
自适应布局
class AdaptiveLayout extends StatelessWidget {@overrideWidget build(BuildContext context) {return LayoutBuilder(builder: (context, constraints) {if (constraints.maxWidth > 600) {// 平板或桌面布局return Row(children: [Expanded(flex: 1, child: Sidebar()),Expanded(flex: 3, child: MainContent()),],);} else {// 手机布局return Column(children: [Expanded(child: MainContent()),BottomNavigation(),],);}},);}
}
4. 动画和过渡
简单动画
class AnimatedContainerExample extends StatefulWidget {@override_AnimatedContainerExampleState createState() => _AnimatedContainerExampleState();
}class _AnimatedContainerExampleState extends State<AnimatedContainerExample> {bool _expanded = false;@overrideWidget build(BuildContext context) {return GestureDetector(onTap: () {setState(() {_expanded = !_expanded;});},child: AnimatedContainer(duration: Duration(milliseconds: 300),width: _expanded ? 200 : 100,height: _expanded ? 200 : 100,color: _expanded ? Colors.blue : Colors.red,child: Center(child: Text('点击动画')),),);}
}
二、代码后端逻辑方面
1. 状态管理
setState方法(简单状态)
class CounterApp extends StatefulWidget {@override_CounterAppState createState() => _CounterAppState();
}class _CounterAppState extends State<CounterApp> {int _counter = 0;/// 增加计数器void _incrementCounter() {setState(() {_counter++;});}/// 减少计数器void _decrementCounter() {setState(() {_counter--;});}@overrideWidget build(BuildContext context) {return Scaffold(body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: [Text('计数器: $_counter', style: TextStyle(fontSize: 24)),SizedBox(height: 20),Row(mainAxisAlignment: MainAxisAlignment.center,children: [ElevatedButton(onPressed: _decrementCounter,child: Text('-'),),SizedBox(width: 20),ElevatedButton(onPressed: _incrementCounter,child: Text('+'),),],),],),),);}
}
Provider状态管理(推荐)
import 'package:provider/provider.dart';// 数据模型
class UserModel with ChangeNotifier {String _name = '';int _age = 0;String get name => _name;int get age => _age;/// 更新用户信息void updateUser(String newName, int newAge) {_name = newName;_age = newAge;notifyListeners(); // 通知监听器更新UI}
}// 使用Provider
class UserProfile extends StatelessWidget {@overrideWidget build(BuildContext context) {return Consumer<UserModel>(builder: (context, user, child) {return Column(children: [Text('姓名: ${user.name}'),Text('年龄: ${user.age}'),ElevatedButton(onPressed: () {user.updateUser('张三', 25);},child: Text('更新用户'),),],);},);}
}
2. 数据持久化
SharedPreferences(本地存储)
import 'package:shared_preferences/shared_preferences.dart';class SettingsService {static const String _themeKey = 'theme';static const String _languageKey = 'language';/// 保存主题设置static Future<void> saveTheme(String theme) async {final prefs = await SharedPreferences.getInstance();await prefs.setString(_themeKey, theme);}/// 获取主题设置static Future<String?> getTheme() async {final prefs = await SharedPreferences.getInstance();return prefs.getString(_themeKey);}/// 保存语言设置static Future<void> saveLanguage(String language) async {final prefs = await SharedPreferences.getInstance();await prefs.setString(_languageKey, language);}/// 获取语言设置static Future<String?> getLanguage() async {final prefs = await SharedPreferences.getInstance();return prefs.getString(_languageKey);}
}
SQLite数据库
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';class DatabaseHelper {static final DatabaseHelper _instance = DatabaseHelper._internal();static Database? _database;DatabaseHelper._internal();factory DatabaseHelper() => _instance;/// 获取数据库实例Future<Database> get database async {if (_database != null) return _database!;_database = await _initDatabase();return _database!;}/// 初始化数据库Future<Database> _initDatabase() async {String path = join(await getDatabasesPath(), 'my_database.db');return await openDatabase(path,version: 1,onCreate: _createTables,);}/// 创建数据表Future<void> _createTables(Database db, int version) async {await db.execute('''CREATE TABLE users(id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT NOT NULL,email TEXT NOT NULL,created_at TEXT)''');}/// 插入用户Future<int> insertUser(Map<String, dynamic> user) async {final db = await database;return await db.insert('users', user);}/// 查询所有用户Future<List<Map<String, dynamic>>> getUsers() async {final db = await database;return await db.query('users');}
}
3. 网络请求
HTTP请求
import 'dart:convert';
import 'package:http/http.dart' as http;class ApiService {static const String baseUrl = 'https://api.example.com';/// 获取用户列表static Future<List<dynamic>> getUsers() async {try {final response = await http.get(Uri.parse('$baseUrl/users'));if (response.statusCode == 200) {return json.decode(response.body);} else {throw Exception('Failed to load users: ${response.statusCode}');}} catch (e) {throw Exception('Network error: $e');}}/// 创建新用户static Future<dynamic> createUser(String name, String email) async {try {final response = await http.post(Uri.parse('$baseUrl/users'),headers: {'Content-Type': 'application/json'},body: json.encode({'name': name, 'email': email}),);if (response.statusCode == 201) {return json.decode(response.body);} else {throw Exception('Failed to create user: ${response.statusCode}');}} catch (e) {throw Exception('Network error: $e');}}
}
使用Dio(第三方HTTP客户端)
import 'package:dio/dio.dart';class DioService {static final Dio _dio = Dio();/// 配置Dio拦截器static void configureDio() {_dio.options.baseUrl = 'https://api.example.com';_dio.options.connectTimeout = Duration(seconds: 30);_dio.options.receiveTimeout = Duration(seconds: 30);// 添加拦截器_dio.interceptors.add(InterceptorsWrapper(onRequest: (options, handler) {// 添加认证tokenoptions.headers['Authorization'] = 'Bearer your_token';return handler.next(options);},onError: (DioError e, handler) {// 错误处理return handler.next(e);},));}/// 获取数据static Future<Response> getData(String endpoint) async {try {return await _dio.get(endpoint);} on DioError catch (e) {throw Exception('Dio error: ${e.message}');}}
}
4. 业务逻辑分离
服务层
/// 用户服务
class UserService {final ApiService _apiService;UserService(this._apiService);/// 获取用户信息Future<User> getUserById(int id) async {final data = await _apiService.get('/users/$id');return User.fromJson(data);}/// 更新用户信息Future<void> updateUser(User user) async {await _apiService.put('/users/${user.id}', user.toJson());}/// 删除用户Future<void> deleteUser(int id) async {await _apiService.delete('/users/$id');}
}
数据模型
/// 用户数据模型
class User {final int id;final String name;final String email;final DateTime createdAt;User({required this.id,required this.name,required this.email,required this.createdAt,});/// 从JSON创建User对象factory User.fromJson(Map<String, dynamic> json) {return User(id: json['id'],name: json['name'],email: json['email'],createdAt: DateTime.parse(json['created_at']),);}/// 将User对象转换为JSONMap<String, dynamic> toJson() {return {'id': id,'name': name,'email': email,'created_at': createdAt.toIso8601String(),};}
}
三、完整示例:待办事项应用
UI部分
import 'package:flutter/material.dart';class TodoApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(title: '待办事项',theme: ThemeData(primarySwatch: Colors.blue,visualDensity: VisualDensity.adaptivePlatformDensity,),home: TodoListPage(),);}
}class TodoListPage extends StatefulWidget {@override_TodoListPageState createState() => _TodoListPageState();
}class _TodoListPageState extends State<TodoListPage> {final List<TodoItem> _todos = [];final TextEditingController _textController = TextEditingController();@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text('待办事项'),actions: [IconButton(icon: Icon(Icons.delete_sweep),onPressed: _clearCompleted,),],),body: Column(children: [// 添加新待办事项Padding(padding: EdgeInsets.all(16.0),child: Row(children: [Expanded(child: TextField(controller: _textController,decoration: InputDecoration(hintText: '输入新的待办事项',border: OutlineInputBorder(),),),),SizedBox(width: 8),ElevatedButton(onPressed: _addTodo,child: Text('添加'),),],),),// 待办事项列表Expanded(child: ListView.builder(itemCount: _todos.length,itemBuilder: (context, index) {final todo = _todos[index];return Dismissible(key: Key(todo.id),onDismissed: (direction) {_removeTodo(index);},background: Container(color: Colors.red),child: ListTile(leading: Checkbox(value: todo.completed,onChanged: (value) {_toggleTodo(index);},),title: Text(todo.title,style: todo.completed? TextStyle(decoration: TextDecoration.lineThrough): null,),trailing: IconButton(icon: Icon(Icons.edit),onPressed: () => _editTodo(index),),),);},),),],),);}void _addTodo() {if (_textController.text.isNotEmpty) {setState(() {_todos.add(TodoItem(id: DateTime.now().millisecondsSinceEpoch.toString(),title: _textController.text,completed: false,));_textController.clear();});}}void _removeTodo(int index) {setState(() {_todos.removeAt(index);});}void _toggleTodo(int index) {setState(() {_todos[index].completed = !_todos[index].completed;});}void _editTodo(int index) {showDialog(context: context,builder: (context) {final controller = TextEditingController(text: _todos[index].title);return AlertDialog(title: Text('编辑待办事项'),content: TextField(controller: controller),actions: [TextButton(onPressed: () => Navigator.pop(context),child: Text('取消'),),TextButton(onPressed: () {setState(() {_todos[index].title = controller.text;});Navigator.pop(context);},child: Text('保存'),),],);},);}void _clearCompleted() {setState(() {_todos.removeWhere((todo) => todo.completed);});}
}class TodoItem {final String id;String title;bool completed;TodoItem({required this.id,required this.title,required this.completed,});
}
后端逻辑部分
/// 待办事项服务
class TodoService {final DatabaseHelper _databaseHelper;TodoService(this._databaseHelper);/// 获取所有待办事项Future<List<TodoItem>> getTodos() async {// 从数据库获取数据final data = await _databaseHelper.getTodos();return data.map((item) => TodoItem.fromJson(item)).toList();}/// 保存待办事项Future<void> saveTodo(TodoItem todo) async {await _databaseHelper.insertTodo(todo.toJson());}/// 删除待办事项Future<void> deleteTodo(String id) async {await _databaseHelper.deleteTodo(id);}/// 更新待办事项Future<void> updateTodo(TodoItem todo) async {await _databaseHelper.updateTodo(todo.toJson());}
}
四、最佳实践
UI开发最佳实践
- 组件化:将UI拆分为可重用的组件
- 响应式设计:适配不同屏幕尺寸
- 一致性:保持设计风格统一
- 性能优化:避免不必要的重绘
代码逻辑最佳实践
- 分离关注点:UI、业务逻辑、数据层分离
- 错误处理:完善的异常处理机制
- 测试驱动:编写单元测试和集成测试
- 代码规范:遵循Dart代码规范
总结
Flutter开发需要同时关注UI设计和代码逻辑。UI方面要掌握Widget的使用、布局技巧和动画效果;代码逻辑方面要理解状态管理、数据持久化和网络请求等核心概念。通过合理的架构设计,可以开发出高性能、易维护的Flutter应用。