Flutter 开发指南
简介
Flutter 是 Google 开发的开源 UI 框架,用于构建跨平台的移动应用。它使用 Dart 语言,提供了丰富的组件库和工具,可以快速构建美观、高性能的应用程序。本文将介绍 Flutter 的核心概念、开发流程和最佳实践。
环境搭建
1. 开发环境配置
bash
# 安装 Flutter SDK
# 从 https://flutter.dev/docs/get-started/install 下载并解压
# 配置环境变量
export PATH="$PATH:`pwd`/flutter/bin"
# 安装依赖
flutter doctor
# 安装 Android Studio 和 Xcode
# 从官网下载并安装2. 创建项目
bash
# 创建新项目
flutter create my_app
# 运行项目
cd my_app
flutter run基础组件
1. 核心组件
dart
import 'package:flutter/material.dart';
class BasicComponents extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('基础组件示例'),
),
body: SingleChildScrollView(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 文本组件
Text(
'这是一段文本',
style: TextStyle(
fontSize: 16.0,
color: Colors.black,
),
),
SizedBox(height: 16.0),
// 图片组件
Image.network(
'https://example.com/image.jpg',
width: double.infinity,
height: 200.0,
fit: BoxFit.cover,
),
SizedBox(height: 16.0),
// 输入框组件
TextField(
decoration: InputDecoration(
hintText: '请输入内容',
border: OutlineInputBorder(),
),
onChanged: (value) {
print('输入内容: $value');
},
),
SizedBox(height: 16.0),
// 按钮组件
ElevatedButton(
onPressed: () {
print('按钮被点击');
},
child: Text('点击我'),
),
],
),
),
);
}
}2. 列表组件
dart
import 'package:flutter/material.dart';
class ListExample extends StatelessWidget {
final List<String> items = List.generate(20, (index) => '项目 ${index + 1}');
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('列表示例'),
),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
leading: Icon(Icons.star),
title: Text(items[index]),
subtitle: Text('这是第 ${index + 1} 个项目'),
trailing: Icon(Icons.arrow_forward_ios),
onTap: () {
print('点击了 ${items[index]}');
},
);
},
),
);
}
}
// 网格列表
class GridExample extends StatelessWidget {
final List<String> items = List.generate(20, (index) => '项目 ${index + 1}');
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('网格列表示例'),
),
body: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0,
),
itemCount: items.length,
itemBuilder: (context, index) {
return Card(
child: Center(
child: Text(items[index]),
),
);
},
),
);
}
}状态管理
1. Provider
dart
// 定义数据模型
class User {
final String name;
final int age;
User({this.name, this.age});
}
// 定义 Provider
class UserProvider with ChangeNotifier {
User _user;
User get user => _user;
void updateUser(User user) {
_user = user;
notifyListeners();
}
}
// 使用 Provider
void main() {
runApp(
ChangeNotifierProvider(
create: (context) => UserProvider(),
child: MyApp(),
),
);
}
class UserProfile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<UserProvider>(
builder: (context, userProvider, child) {
final user = userProvider.user;
return Column(
children: [
Text('姓名: ${user?.name ?? "未设置"}'),
Text('年龄: ${user?.age ?? 0}'),
ElevatedButton(
onPressed: () {
userProvider.updateUser(User(name: '张三', age: 25));
},
child: Text('更新用户信息'),
),
],
);
},
);
}
}2. Bloc
dart
// 定义事件
abstract class UserEvent {}
class UpdateUser extends UserEvent {
final User user;
UpdateUser(this.user);
}
// 定义状态
class UserState {
final User user;
UserState(this.user);
}
// 定义 Bloc
class UserBloc extends Bloc<UserEvent, UserState> {
UserBloc() : super(UserState(null));
@override
Stream<UserState> mapEventToState(UserEvent event) async* {
if (event is UpdateUser) {
yield UserState(event.user);
}
}
}
// 使用 Bloc
class UserProfile extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocBuilder<UserBloc, UserState>(
builder: (context, state) {
return Column(
children: [
Text('姓名: ${state.user?.name ?? "未设置"}'),
Text('年龄: ${state.user?.age ?? 0}'),
ElevatedButton(
onPressed: () {
context.read<UserBloc>().add(
UpdateUser(User(name: '张三', age: 25)),
);
},
child: Text('更新用户信息'),
),
],
);
},
);
}
}导航
1. 基本导航
dart
class NavigationExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeScreen(),
routes: {
'/detail': (context) => DetailScreen(),
'/settings': (context) => SettingsScreen(),
},
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('首页'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/detail');
},
child: Text('跳转到详情页'),
),
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/settings');
},
child: Text('跳转到设置页'),
),
],
),
),
);
}
}2. 命名路由
dart
class AppRoutes {
static const String home = '/';
static const String detail = '/detail';
static const String settings = '/settings';
}
class AppRouter {
static Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case AppRoutes.home:
return MaterialPageRoute(builder: (_) => HomeScreen());
case AppRoutes.detail:
return MaterialPageRoute(builder: (_) => DetailScreen());
case AppRoutes.settings:
return MaterialPageRoute(builder: (_) => SettingsScreen());
default:
return MaterialPageRoute(
builder: (_) => Scaffold(
body: Center(
child: Text('未找到路由: ${settings.name}'),
),
),
);
}
}
}网络请求
1. HTTP 请求
dart
import 'package:http/http.dart' as http;
import 'dart:convert';
class ApiService {
static const String baseUrl = 'https://api.example.com';
Future<List<dynamic>> getList() async {
final response = await http.get(Uri.parse('$baseUrl/list'));
if (response.statusCode == 200) {
return json.decode(response.body);
} else {
throw Exception('加载失败');
}
}
Future<Map<String, dynamic>> getDetail(int id) async {
final response = await http.get(Uri.parse('$baseUrl/detail/$id'));
if (response.statusCode == 200) {
return json.decode(response.body);
} else {
throw Exception('加载失败');
}
}
Future<void> postData(Map<String, dynamic> data) async {
final response = await http.post(
Uri.parse('$baseUrl/submit'),
headers: {'Content-Type': 'application/json'},
body: json.encode(data),
);
if (response.statusCode != 200) {
throw Exception('提交失败');
}
}
}2. 使用 Dio
dart
import 'package:dio/dio.dart';
class DioService {
final Dio _dio = Dio(BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: 5000,
receiveTimeout: 3000,
));
Future<List<dynamic>> getList() async {
try {
final response = await _dio.get('/list');
return response.data;
} on DioError catch (e) {
throw Exception('加载失败: ${e.message}');
}
}
Future<Map<String, dynamic>> getDetail(int id) async {
try {
final response = await _dio.get('/detail/$id');
return response.data;
} on DioError catch (e) {
throw Exception('加载失败: ${e.message}');
}
}
Future<void> postData(Map<String, dynamic> data) async {
try {
await _dio.post('/submit', data: data);
} on DioError catch (e) {
throw Exception('提交失败: ${e.message}');
}
}
}本地存储
1. SharedPreferences
dart
import 'package:shared_preferences/shared_preferences.dart';
class LocalStorage {
static Future<void> saveString(String key, String value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(key, value);
}
static Future<String> getString(String key) async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(key);
}
static Future<void> remove(String key) async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove(key);
}
}
// 使用示例
class SettingsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('设置'),
),
body: FutureBuilder<String>(
future: LocalStorage.getString('username'),
builder: (context, snapshot) {
return Column(
children: [
Text('用户名: ${snapshot.data ?? "未设置"}'),
ElevatedButton(
onPressed: () async {
await LocalStorage.saveString('username', '张三');
},
child: Text('保存用户名'),
),
],
);
},
),
);
}
}2. SQLite
dart
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DatabaseHelper {
static final DatabaseHelper _instance = DatabaseHelper._internal();
static Database _database;
factory DatabaseHelper() => _instance;
DatabaseHelper._internal();
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: _onCreate,
);
}
Future<void> _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE users(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
age INTEGER
)
''');
}
Future<int> insertUser(Map<String, dynamic> user) async {
Database db = await database;
return await db.insert('users', user);
}
Future<List<Map<String, dynamic>>> getUsers() async {
Database db = await database;
return await db.query('users');
}
}性能优化
1. 列表优化
dart
class OptimizedList extends StatelessWidget {
final List<String> items = List.generate(1000, (index) => '项目 ${index + 1}');
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(items[index]),
);
},
);
}
}
// 使用 const 构造函数
class OptimizedWidget extends StatelessWidget {
const OptimizedWidget({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Text(
'优化后的文本',
style: TextStyle(
fontSize: 16.0,
color: Colors.black,
),
);
}
}2. 图片优化
dart
import 'package:cached_network_image/cached_network_image.dart';
class OptimizedImage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CachedNetworkImage(
imageUrl: 'https://example.com/image.jpg',
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
fit: BoxFit.cover,
);
}
}
// 使用 Image.asset 预加载图片
class PreloadImages extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: precacheImage(
AssetImage('assets/images/background.jpg'),
context,
),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Image.asset('assets/images/background.jpg');
}
return CircularProgressIndicator();
},
);
}
}发布部署
1. Android 发布
bash
# 生成签名密钥
keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload
# 配置签名信息
# 在 android/key.properties 中添加配置
storePassword=<密码>
keyPassword=<密码>
keyAlias=upload
storeFile=<密钥库文件路径>
# 构建发布版本
flutter build apk --release
# 生成 APK 文件
# 在 build/app/outputs/flutter-apk/ 目录下2. iOS 发布
bash
# 配置证书和描述文件
# 在 Xcode 中配置
# 构建发布版本
flutter build ios --release
# 使用 Xcode 上传到 App Store
# 打开 ios/Runner.xcworkspace
# 选择 Product > Archive最佳实践
1. 项目结构
bash
lib/
├── main.dart # 应用入口
├── app/ # 应用配置
├── screens/ # 页面
├── widgets/ # 可复用组件
├── models/ # 数据模型
├── services/ # 服务
├── utils/ # 工具函数
└── constants/ # 常量2. 代码规范
yaml
# analysis_options.yaml
include: package:flutter_lints/flutter.yaml
linter:
rules:
- always_declare_return_types
- avoid_empty_else
- avoid_print
- avoid_unused_constructor_parameters
- await_only_futures
- camel_case_types
- cancel_subscriptions
- constant_identifier_names
- control_flow_in_finally
- empty_catches
- empty_constructor_bodies
- empty_statements
- hash_and_equals
- implementation_imports
- library_names
- library_prefixes
- non_constant_identifier_names
- package_api_docs
- package_names
- package_prefixed_library_names
- prefer_const_constructors
- prefer_final_fields
- prefer_is_not_empty
- prefer_single_quotes
- prefer_typing_uninitialized_variables
- sort_child_properties_last
- test_types_in_equals
- throw_in_finally
- unnecessary_brace_in_string_interps
- unnecessary_getters_setters
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_statements
- unnecessary_this
- unrelated_type_equality_checks
- use_rethrow_when_possible
- valid_regexps总结
Flutter 提供了强大的跨平台开发能力,通过合理使用框架特性和遵循最佳实践,可以构建出高性能、可维护的移动应用。