flutter-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFlutter Best Practices
Flutter 最佳实践
Expert guidelines for building production-ready Flutter applications across mobile, web, and desktop platforms.
面向移动、Web和桌面平台,构建可投入生产的Flutter应用的专家指南。
Quick Start
快速入门
When starting a new Flutter project or reviewing code:
- Architecture: Use feature-first folder structure with clean separation of concerns
- State Management: Prefer Riverpod for new projects; use BLoC for complex async flows
- Navigation: Use go_router for deep linking and web support
- Testing: Follow the testing pyramid - unit → widget → integration
- Performance: Use constructors,
const, and proper disposal patternsListView.builder
当启动新Flutter项目或审查代码时:
- 架构:采用以功能为优先的文件夹结构,清晰分离关注点
- 状态管理:新项目优先使用Riverpod;复杂异步流场景使用BLoC
- 导航:使用go_router实现深度链接与Web支持
- 测试:遵循测试金字塔 - 单元测试 → 组件测试 → 集成测试
- 性能:使用构造函数、
const以及正确的资源销毁模式ListView.builder
Architecture Patterns
架构模式
Three-Layer Architecture
三层架构
Organize code into distinct layers for maintainability:
lib/
├── features/
│ └── feature_name/
│ ├── presentation/ # UI widgets, screens
│ ├── domain/ # Business logic, use cases, entities
│ └── data/ # Repositories, data sources, models
├── core/
│ ├── theme/ # AppTheme, colors, typography
│ ├── router/ # GoRouter configuration
│ └── utils/ # Shared utilities
└── main.dartPrinciples:
- Dependency Rule: Higher layers depend on lower layers only
- Feature API Interface: Hide implementation details behind interfaces
- Repository Pattern: Abstract data access for testability
将代码组织为不同层级以提升可维护性:
lib/
├── features/
│ └── feature_name/
│ ├── presentation/ # UI widgets, screens
│ ├── domain/ # Business logic, use cases, entities
│ └── data/ # Repositories, data sources, models
├── core/
│ ├── theme/ # AppTheme, colors, typography
│ ├── router/ # GoRouter configuration
│ └── utils/ # Shared utilities
└── main.dart原则:
- 依赖规则:高层级仅依赖低层级
- 功能API接口:通过接口隐藏实现细节
- 仓库模式:抽象数据访问以提升可测试性
Widget Architecture
Widget架构
Widget Types Decision Tree:
| Scenario | Use |
|---|---|
| Static UI, no changing state | |
| Interactive UI with local state | |
| Data shared down the tree | |
| Complex reusable UI component | Custom widget class |
StatefulWidget Lifecycle (Critical):
dart
class _MyWidgetState extends State<MyWidget> {
void initState() {
super.initState();
// One-time initialization (controllers, subscriptions)
}
void didChangeDependencies() {
super.didChangeDependencies();
// Called when InheritedWidget dependencies change
}
Widget build(BuildContext context) {
// Keep fast and synchronous - no heavy computation
return Container();
}
void dispose() {
// CRITICAL: Dispose controllers, cancel subscriptions
_controller.dispose();
_subscription?.cancel();
super.dispose();
}
}Golden Rule: Always dispose controllers, scroll controllers, text controllers, and cancel stream subscriptions.
Widget类型决策树:
| 场景 | 使用 |
|---|---|
| 静态UI,无状态变化 | |
| 带本地状态的交互式UI | |
| 数据在组件树中向下共享 | |
| 复杂可复用UI组件 | 自定义Widget类 |
StatefulWidget生命周期(关键):
dart
class _MyWidgetState extends State<MyWidget> {
void initState() {
super.initState();
// One-time initialization (controllers, subscriptions)
}
void didChangeDependencies() {
super.didChangeDependencies();
// Called when InheritedWidget dependencies change
}
Widget build(BuildContext context) {
// Keep fast and synchronous - no heavy computation
return Container();
}
void dispose() {
// CRITICAL: Dispose controllers, cancel subscriptions
_controller.dispose();
_subscription?.cancel();
super.dispose();
}
}黄金法则:务必销毁控制器、滚动控制器、文本控制器,并取消流订阅。
State Management
状态管理
Riverpod (Recommended for New Projects)
Riverpod(新项目推荐)
Setup:
dart
// main.dart
void main() {
runApp(ProviderScope(child: MyApp()));
}
// providers.dart
final counterProvider = StateProvider<int>((ref) => 0);
final userProvider = FutureProvider<User>((ref) async {
return await ref.watch(authRepositoryProvider).getCurrentUser();
});
class TodoList extends _$TodoList {
List<Todo> build() => [];
void add(Todo todo) => state = [...state, todo];
void toggle(String id) {
state = state.map((t) =>
t.id == id ? t.copyWith(completed: !t.completed) : t
).toList();
}
}Usage Patterns:
dart
class MyWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
// Watch for UI updates
final count = ref.watch(counterProvider);
final asyncUser = ref.watch(userProvider);
final todos = ref.watch(todoListProvider);
// Read for one-time access (callbacks)
void onPressed() {
ref.read(todoListProvider.notifier).add(newTodo);
}
return asyncUser.when(
data: (user) => Text(user.name),
loading: () => CircularProgressIndicator(),
error: (err, stack) => ErrorWidget(err),
);
}
}Key Methods:
- : Rebuild widget when provider changes
ref.watch(provider) - : Get value once (use in callbacks, not build)
ref.read(provider) - : Listen for changes and perform side effects
ref.listen(provider)
配置:
dart
// main.dart
void main() {
runApp(ProviderScope(child: MyApp()));
}
// providers.dart
final counterProvider = StateProvider<int>((ref) => 0);
final userProvider = FutureProvider<User>((ref) async {
return await ref.watch(authRepositoryProvider).getCurrentUser();
});
class TodoList extends _$TodoList {
List<Todo> build() => [];
void add(Todo todo) => state = [...state, todo];
void toggle(String id) {
state = state.map((t) =>
t.id == id ? t.copyWith(completed: !t.completed) : t
).toList();
}
}使用模式:
dart
class MyWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
// Watch for UI updates
final count = ref.watch(counterProvider);
final asyncUser = ref.watch(userProvider);
final todos = ref.watch(todoListProvider);
// Read for one-time access (callbacks)
void onPressed() {
ref.read(todoListProvider.notifier).add(newTodo);
}
return asyncUser.when(
data: (user) => Text(user.name),
loading: () => CircularProgressIndicator(),
error: (err, stack) => ErrorWidget(err),
);
}
}核心方法:
- :当provider变化时重建组件
ref.watch(provider) - :一次性获取值(用于回调,不要在build中使用)
ref.read(provider) - :监听变化并执行副作用
ref.listen(provider)
BLoC Pattern (For Complex Async Flows)
BLoC模式(复杂异步流场景)
When to use BLoC:
- Complex event-driven architectures
- Apps with heavy business logic separation requirements
- Teams familiar with reactive programming
dart
// Events
abstract class CounterEvent {}
class CounterIncrementPressed extends CounterEvent {}
// States
class CounterState {
final int count;
CounterState(this.count);
}
// BLoC
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterState(0)) {
on<CounterIncrementPressed>((event, emit) {
emit(CounterState(state.count + 1));
});
}
}
// Usage
BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Text('Count: ${state.count}');
},
)何时使用BLoC:
- 复杂事件驱动架构
- 对业务逻辑分离要求高的应用
- 熟悉响应式编程的团队
dart
// Events
abstract class CounterEvent {}
class CounterIncrementPressed extends CounterEvent {}
// States
class CounterState {
final int count;
CounterState(this.count);
}
// BLoC
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterState(0)) {
on<CounterIncrementPressed>((event, emit) {
emit(CounterState(state.count + 1));
});
}
}
// Usage
BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Text('Count: ${state.count}');
},
)State Selection Guide
状态选择指南
| Use Case | Solution |
|---|---|
| Simple local widget state | |
| Single value shared across widgets | |
| Async data (API calls) | |
| Stream-based data | |
| Complex state with business logic | |
| Global app state | Riverpod with scoped providers |
| 使用场景 | 解决方案 |
|---|---|
| 简单本地组件状态 | |
| 跨组件共享的单一值 | |
| 异步数据(API调用) | |
| 基于流的数据 | |
| 带业务逻辑的复杂状态 | |
| 全局应用状态 | 带作用域Provider的Riverpod |
Navigation
导航
go_router (Recommended)
go_router(推荐)
Setup:
dart
final _router = GoRouter(
initialLocation: '/',
redirect: (context, state) {
// Auth guard
final isLoggedIn = authState.isAuthenticated;
final isLoggingIn = state.matchedLocation == '/login';
if (!isLoggedIn && !isLoggingIn) return '/login';
if (isLoggedIn && isLoggingIn) return '/';
return null;
},
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomeScreen(),
routes: [
GoRoute(
path: 'details/:id',
builder: (context, state) => DetailScreen(
id: state.pathParameters['id']!,
),
),
],
),
GoRoute(
path: '/login',
builder: (context, state) => LoginScreen(),
),
],
);
// In MaterialApp
MaterialApp.router(routerConfig: _router);Navigation:
dart
// Navigate
context.go('/details/123');
context.push('/details/123'); // Preserves navigation stack
context.pop();
// With query parameters
context.goNamed('details', queryParameters: {'tab': 'reviews'});Deep Linking Setup:
Android ():
AndroidManifest.xmlxml
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https" android:host="yourdomain.com"/>
</intent-filter>iOS ():
info.plistxml
<key>FlutterDeepLinkingEnabled</key>
<true/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>yourscheme</string>
</array>
</dict>
</array>配置:
dart
final _router = GoRouter(
initialLocation: '/',
redirect: (context, state) {
// Auth guard
final isLoggedIn = authState.isAuthenticated;
final isLoggingIn = state.matchedLocation == '/login';
if (!isLoggedIn && !isLoggingIn) return '/login';
if (isLoggedIn && isLoggingIn) return '/';
return null;
},
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomeScreen(),
routes: [
GoRoute(
path: 'details/:id',
builder: (context, state) => DetailScreen(
id: state.pathParameters['id']!,
),
),
],
),
GoRoute(
path: '/login',
builder: (context, state) => LoginScreen(),
),
],
);
// In MaterialApp
MaterialApp.router(routerConfig: _router);导航操作:
dart
// 导航
context.go('/details/123');
context.push('/details/123'); // 保留导航栈
context.pop();
// 带查询参数
context.goNamed('details', queryParameters: {'tab': 'reviews'});深度链接配置:
Android():
AndroidManifest.xmlxml
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https" android:host="yourdomain.com"/>
</intent-filter>iOS():
info.plistxml
<key>FlutterDeepLinkingEnabled</key>
<true/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>yourscheme</string>
</array>
</dict>
</array>Widget Patterns
Widget模式
Layout Essentials
布局基础
dart
// Common layout patterns
Column( // Vertical layout
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [...],
)
Row( // Horizontal layout
children: [
Expanded(flex: 2, child: ...), // Takes 2/3 space
Expanded(flex: 1, child: ...), // Takes 1/3 space
],
)
Stack( // Overlapping widgets
children: [
Positioned.fill(child: Background()),
Align(alignment: Alignment.bottomCenter, child: ...),
],
)
// Responsive layouts
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 600) {
return DesktopLayout();
}
return MobileLayout();
},
)dart
// 常见布局模式
Column( // 垂直布局
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [...],
)
Row( // 水平布局
children: [
Expanded(flex: 2, child: ...), // 占2/3空间
Expanded(flex: 1, child: ...), // 占1/3空间
],
)
Stack( // 重叠组件
children: [
Positioned.fill(child: Background()),
Align(alignment: Alignment.bottomCenter, child: ...),
],
)
// 响应式布局
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 600) {
return DesktopLayout();
}
return MobileLayout();
},
)List Optimization
列表优化
ALWAYS use builder for large/infinite lists:
dart
// Good - lazy loading, only builds visible items
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) => ListTile(
title: Text(items[index].title),
),
)
// For grids with images
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
),
itemCount: items.length,
itemBuilder: (context, index) => ImageCard(items[index]),
)
// With custom scroll effects
CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(title: Text('Title')),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => ListTile(...),
childCount: items.length,
),
),
],
)大型/无限列表务必使用builder:
dart
// 推荐 - 懒加载,仅构建可见项
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) => ListTile(
title: Text(items[index].title),
),
)
// 带图片的网格
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
),
itemCount: items.length,
itemBuilder: (context, index) => ImageCard(items[index]),
)
// 自定义滚动效果
CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 200,
flexibleSpace: FlexibleSpaceBar(title: Text('Title')),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => ListTile(...),
childCount: items.length,
),
),
],
)Form Handling
表单处理
dart
class _MyFormState extends State<MyForm> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
void dispose() {
_nameController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _nameController,
decoration: InputDecoration(labelText: 'Name'),
validator: (value) {
if (value?.isEmpty ?? true) return 'Required';
return null;
},
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
// Process form
}
},
child: Text('Submit'),
),
],
),
);
}
}dart
class _MyFormState extends State<MyForm> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
void dispose() {
_nameController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _nameController,
decoration: InputDecoration(labelText: 'Name'),
validator: (value) {
if (value?.isEmpty ?? true) return 'Required';
return null;
},
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
// Process form
}
},
child: Text('Submit'),
),
],
),
);
}
}Performance Optimization
性能优化
Critical Performance Rules
关键性能规则
| Rule | Implementation | Priority |
|---|---|---|
Use | | Critical |
| Use builders for lists | | Critical |
| Dispose controllers | | Critical |
| Cache expensive operations | | High |
Use | Wrap animated widgets | Medium |
| Avoid clipping | Minimize | Medium |
| 规则 | 实现方式 | 优先级 |
|---|---|---|
使用 | | 关键 |
| 列表使用builder | | 关键 |
| 销毁控制器 | | 关键 |
| 缓存昂贵操作 | 使用 | 高 |
使用 | 包裹动画组件 | 中 |
| 避免裁剪 | 减少 | 中 |
Memory Management
内存管理
dart
// Always check mounted before setState in async
void _loadData() async {
final data = await fetchData();
if (mounted) { // CRITICAL
setState(() => _data = data);
}
}
// Limit image cache for memory-constrained apps
PaintingBinding.instance.imageCache.maximumSize = 100;
PaintingBinding.instance.imageCache.maximumSizeBytes = 50 * 1024 * 1024;
// Use weak references for long-lived callbacks
final weakRef = WeakReference(object);dart
// 异步操作中调用setState前务必检查mounted
void _loadData() async {
final data = await fetchData();
if (mounted) { // 关键
setState(() => _data = data);
}
}
// 对内存受限应用限制图片缓存
PaintingBinding.instance.imageCache.maximumSize = 100;
PaintingBinding.instance.imageCache.maximumSizeBytes = 50 * 1024 * 1024;
// 长期回调使用弱引用
final weakRef = WeakReference(object);Isolate Usage
隔离区使用
Offload heavy computation from UI thread:
dart
// Heavy computation
final result = await compute(parseJson, largeJsonString);
// For complex parsing
Future<List<Model>> parseModels(String jsonString) async {
return compute((str) {
final decoded = json.decode(str) as List;
return decoded.map((e) => Model.fromJson(e)).toList();
}, jsonString);
}将繁重计算从UI线程卸载:
dart
// 繁重计算
final result = await compute(parseJson, largeJsonString);
// 复杂解析
Future<List<Model>> parseModels(String jsonString) async {
return compute((str) {
final decoded = json.decode(str) as List;
return decoded.map((e) => Model.fromJson(e)).toList();
}, jsonString);
}DevTools Profiling
DevTools性能分析
Enable these flags for debugging:
dart
// In main.dart for debugging
void main() {
// Visualize widget rebuilds
debugRepaintRainbowEnabled = true;
// Show paint bounds
debugPaintSizeEnabled = true;
runApp(MyApp());
}Key DevTools Views:
- Widget Rebuild Counts: Identify excessive rebuilds
- Performance: Frame timing, shader compilation
- Memory: Heap snapshots, allocation tracking
- Network: API call monitoring
启用以下标记用于调试:
dart
// main.dart中用于调试
void main() {
// 可视化组件重建
debugRepaintRainbowEnabled = true;
// 显示绘制边界
debugPaintSizeEnabled = true;
runApp(MyApp());
}DevTools核心视图:
- 组件重建次数:识别过度重建问题
- 性能:帧时序、着色器编译
- 内存:堆快照、分配跟踪
- 网络:API调用监控
Testing
测试
Testing Pyramid
测试金字塔
▲ Integration (Full flows)
╱ ╲
╱ ╲ Widget (UI components)
╱ ╲
╱ ╲
╱_________╲
Unit (Logic, pure Dart) ▲ 集成测试(完整流程)
╱ ╲
╱ ╲ 组件测试(UI组件)
╱ ╲
╱ ╲
╱_________╲
单元测试(逻辑、纯Dart代码)Unit Testing
单元测试
dart
test('can calculate total price', () {
// Arrange
final cart = Cart(items: [
Item(price: 10, quantity: 2),
Item(price: 5, quantity: 1),
]);
// Act
final total = cart.total;
// Assert
expect(total, equals(25));
});
// With Riverpod
test('counter increments', () {
final container = ProviderContainer();
addTearDown(container.dispose);
final notifier = container.read(counterProvider.notifier);
notifier.state = 5;
notifier.state++;
expect(container.read(counterProvider), equals(6));
});dart
test('can calculate total price', () {
// Arrange
final cart = Cart(items: [
Item(price: 10, quantity: 2),
Item(price: 5, quantity: 1),
]);
// Act
final total = cart.total;
// Assert
expect(total, equals(25));
});
// 结合Riverpod测试
test('counter increments', () {
final container = ProviderContainer();
addTearDown(container.dispose);
final notifier = container.read(counterProvider.notifier);
notifier.state = 5;
notifier.state++;
expect(container.read(counterProvider), equals(6));
});Widget Testing
组件测试
dart
testWidgets('displays user name', (WidgetTester tester) async {
// Arrange
await tester.pumpWidget(
MaterialApp(
home: UserProfile(user: User(name: 'John')),
),
);
// Act & Assert
expect(find.text('John'), findsOneWidget);
expect(find.byType(CircularProgressIndicator), findsNothing);
});
testWidgets('tapping button increments counter', (tester) async {
await tester.pumpWidget(MyApp());
await tester.tap(find.byIcon(Icons.add));
await tester.pumpAndSettle();
expect(find.text('1'), findsOneWidget);
});dart
testWidgets('displays user name', (WidgetTester tester) async {
// Arrange
await tester.pumpWidget(
MaterialApp(
home: UserProfile(user: User(name: 'John')),
),
);
// Act & Assert
expect(find.text('John'), findsOneWidget);
expect(find.byType(CircularProgressIndicator), findsNothing);
});
testWidgets('tapping button increments counter', (tester) async {
await tester.pumpWidget(MyApp());
await tester.tap(find.byIcon(Icons.add));
await tester.pumpAndSettle();
expect(find.text('1'), findsOneWidget);
});Golden Tests (Visual Regression)
黄金测试(视觉回归)
dart
testGoldens('UserProfile renders correctly', (tester) async {
final builder = GoldenBuilder.grid(columns: 2)
..addScenario('Light', UserProfile(user: mockUser))
..addScenario('Dark', Theme(data: darkTheme, child: UserProfile(user: mockUser)));
await tester.pumpWidgetBuilder(builder.build());
await screenMatchesGolden(tester, 'user_profile');
});
// Update goldens: flutter test --update-goldens --tags=goldendart
testGoldens('UserProfile renders correctly', (tester) async {
final builder = GoldenBuilder.grid(columns: 2)
..addScenario('Light', UserProfile(user: mockUser))
..addScenario('Dark', Theme(data: darkTheme, child: UserProfile(user: mockUser)));
await tester.pumpWidgetBuilder(builder.build());
await screenMatchesGolden(tester, 'user_profile');
});
// 更新黄金文件: flutter test --update-goldens --tags=goldenIntegration Testing
集成测试
dart
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('full login flow', (tester) async {
app.main();
await tester.pumpAndSettle();
await tester.enterText(find.byType(TextField).first, 'user@example.com');
await tester.enterText(find.byType(TextField).last, 'password');
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(find.text('Welcome'), findsOneWidget);
});
}dart
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('full login flow', (tester) async {
app.main();
await tester.pumpAndSettle();
await tester.enterText(find.byType(TextField).first, 'user@example.com');
await tester.enterText(find.byType(TextField).last, 'password');
await tester.tap(find.byType(ElevatedButton));
await tester.pumpAndSettle();
expect(find.text('Welcome'), findsOneWidget);
});
}Networking
网络请求
HTTP Service Pattern
HTTP服务模式
dart
class ApiService {
final Dio _dio;
ApiService({Dio? dio}) : _dio = dio ?? Dio(BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: Duration(seconds: 5),
receiveTimeout: Duration(seconds: 3),
)) {
_dio.interceptors.add(AuthInterceptor());
_dio.interceptors.add(LogInterceptor());
}
Future<List<User>> getUsers() async {
final response = await _dio.get('/users');
return (response.data as List)
.map((json) => User.fromJson(json))
.toList();
}
}
// With Riverpod
final apiServiceProvider = Provider<ApiService>((ref) {
return ApiService();
});
final usersProvider = FutureProvider<List<User>>((ref) async {
final api = ref.watch(apiServiceProvider);
return api.getUsers();
});dart
class ApiService {
final Dio _dio;
ApiService({Dio? dio}) : _dio = dio ?? Dio(BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: Duration(seconds: 5),
receiveTimeout: Duration(seconds: 3),
)) {
_dio.interceptors.add(AuthInterceptor());
_dio.interceptors.add(LogInterceptor());
}
Future<List<User>> getUsers() async {
final response = await _dio.get('/users');
return (response.data as List)
.map((json) => User.fromJson(json))
.toList();
}
}
// 结合Riverpod
final apiServiceProvider = Provider<ApiService>((ref) {
return ApiService();
});
final usersProvider = FutureProvider<List<User>>((ref) async {
final api = ref.watch(apiServiceProvider);
return api.getUsers();
});JSON Serialization
JSON序列化
dart
// With freezed (recommended)
class User with _$User {
const factory User({
required String id,
required String name,
([]) List<String> tags,
DateTime? createdAt,
}) = _User;
factory User.fromJson(Map<String, dynamic> json) =>
_$UserFromJson(json);
}
// Manual approach
class User {
final String id;
final String name;
User({required this.id, required this.name});
factory User.fromJson(Map<String, dynamic> json) => User(
id: json['id'] as String,
name: json['name'] as String,
);
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
};
}dart
// 使用freezed(推荐)
class User with _$User {
const factory User({
required String id,
required String name,
([]) List<String> tags,
DateTime? createdAt,
}) = _User;
factory User.fromJson(Map<String, dynamic> json) =>
_$UserFromJson(json);
}
// 手动实现
class User {
final String id;
final String name;
User({required this.id, required this.name});
factory User.fromJson(Map<String, dynamic> json) => User(
id: json['id'] as String,
name: json['name'] as String,
);
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
};
}Firebase Integration
Firebase集成
Firebase Setup
Firebase配置
bash
undefinedbash
undefinedInstall FlutterFire CLI
安装FlutterFire CLI
dart pub global activate flutterfire_cli
dart pub global activate flutterfire_cli
Configure project
配置项目
flutterfire configure --project=your-project-id
undefinedflutterfire configure --project=your-project-id
undefinedCloud Messaging (Push Notifications)
云消息推送(推送通知)
dart
class NotificationService {
final FirebaseMessaging _messaging;
NotificationService(this._messaging);
Future<void> initialize() async {
// Request permissions
final settings = await _messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
// Get FCM token
final token = await _messaging.getToken();
await _saveToken(token);
// Listen to token refresh
_messaging.onTokenRefresh.listen(_saveToken);
// Handle foreground messages
FirebaseMessaging.onMessage.listen(_handleForegroundMessage);
// Handle background/terminated
FirebaseMessaging.onMessageOpenedApp.listen(_handleNotificationTap);
final initialMessage = await _messaging.getInitialMessage();
if (initialMessage != null) {
_handleNotificationTap(initialMessage);
}
}
('vm:entry-point')
static Future<void> _firebaseMessagingBackgroundHandler(
RemoteMessage message,
) async {
await Firebase.initializeApp();
// Handle background message
}
}
// Register in main
FirebaseMessaging.onBackgroundMessage(
NotificationService._firebaseMessagingBackgroundHandler,
);dart
class NotificationService {
final FirebaseMessaging _messaging;
NotificationService(this._messaging);
Future<void> initialize() async {
// 请求权限
final settings = await _messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
// 获取FCM令牌
final token = await _messaging.getToken();
await _saveToken(token);
// 监听令牌刷新
_messaging.onTokenRefresh.listen(_saveToken);
// 处理前台消息
FirebaseMessaging.onMessage.listen(_handleForegroundMessage);
// 处理后台/终止状态消息
FirebaseMessaging.onMessageOpenedApp.listen(_handleNotificationTap);
final initialMessage = await _messaging.getInitialMessage();
if (initialMessage != null) {
_handleNotificationTap(initialMessage);
}
}
('vm:entry-point')
static Future<void> _firebaseMessagingBackgroundHandler(
RemoteMessage message,
) async {
await Firebase.initializeApp();
// Handle background message
}
}
// 在main中注册
FirebaseMessaging.onBackgroundMessage(
NotificationService._firebaseMessagingBackgroundHandler,
);Cross-Platform Considerations
跨平台注意事项
Platform Detection
平台检测
dart
import 'dart:io' show Platform;
import 'package:flutter/foundation.dart' show kIsWeb;
bool get isWeb => kIsWeb;
bool get isMobile => !kIsWeb && (Platform.isIOS || Platform.isAndroid);
bool get isDesktop => !kIsWeb && (Platform.isWindows || Platform.isMacOS || Platform.isLinux);
// Conditional UI
if (Platform.isIOS) {
return CupertinoButton(child: Text('Done'), onPressed: () {});
}
return ElevatedButton(child: Text('Done'), onPressed: () {});dart
import 'dart:io' show Platform;
import 'package:flutter/foundation.dart' show kIsWeb;
bool get isWeb => kIsWeb;
bool get isMobile => !kIsWeb && (Platform.isIOS || Platform.isAndroid);
bool get isDesktop => !kIsWeb && (Platform.isWindows || Platform.isMacOS || Platform.isLinux);
// 条件UI
if (Platform.isIOS) {
return CupertinoButton(child: Text('Done'), onPressed: () {});
}
return ElevatedButton(child: Text('Done'), onPressed: () {});Adaptive Layouts
自适应布局
dart
// Using flutter_adaptive_scaffold
AdaptiveLayout(
primaryNavigation: SlotLayout(config: {
Breakpoints.mediumAndUp: SlotLayout.from(
builder: (_) => NavigationRail(destinations: [...]),
),
}),
bottomNavigation: SlotLayout(config: {
Breakpoints.small: SlotLayout.from(
builder: (_) => BottomNavigationBar(items: [...]),
),
}),
body: SlotLayout(config: {
Breakpoints.small: SlotLayout.from(builder: (_) => MobileBody()),
Breakpoints.mediumAndUp: SlotLayout.from(builder: (_) => DesktopBody()),
}),
)dart
// 使用flutter_adaptive_scaffold
AdaptiveLayout(
primaryNavigation: SlotLayout(config: {
Breakpoints.mediumAndUp: SlotLayout.from(
builder: (_) => NavigationRail(destinations: [...]),
),
}),
bottomNavigation: SlotLayout(config: {
Breakpoints.small: SlotLayout.from(
builder: (_) => BottomNavigationBar(items: [...]),
),
}),
body: SlotLayout(config: {
Breakpoints.small: SlotLayout.from(builder: (_) => MobileBody()),
Breakpoints.mediumAndUp: SlotLayout.from(builder: (_) => DesktopBody()),
}),
)Platform Channels
平台通道
Flutter side:
dart
class PlatformChannelService {
static const MethodChannel _channel =
MethodChannel('com.example.app/channel');
Future<String?> getNativeData() async {
try {
final result = await _channel.invokeMethod<String>('getNativeData');
return result;
} on PlatformException catch (e) {
print('Error: ${e.message}');
return null;
}
}
}Flutter端:
dart
class PlatformChannelService {
static const MethodChannel _channel =
MethodChannel('com.example.app/channel');
Future<String?> getNativeData() async {
try {
final result = await _channel.invokeMethod<String>('getNativeData');
return result;
} on PlatformException catch (e) {
print('Error: ${e.message}');
return null;
}
}
}Animations
动画
Implicit Animations
隐式动画
dart
// Simple animations that trigger on value change
AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: isExpanded ? 200 : 100,
height: isExpanded ? 200 : 100,
color: isExpanded ? Colors.red : Colors.blue,
child: child,
)
AnimatedOpacity(
duration: Duration(milliseconds: 200),
opacity: isVisible ? 1.0 : 0.0,
child: child,
)dart
// 值变化时触发的简单动画
AnimatedContainer(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: isExpanded ? 200 : 100,
height: isExpanded ? 200 : 100,
color: isExpanded ? Colors.red : Colors.blue,
child: child,
)
AnimatedOpacity(
duration: Duration(milliseconds: 200),
opacity: isVisible ? 1.0 : 0.0,
child: child,
)Explicit Animations
显式动画
dart
class _MyWidgetState extends State<MyWidget>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
);
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
);
_controller.forward();
}
void dispose() {
_controller.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return FadeTransition(
opacity: _animation,
child: ScaleTransition(
scale: _animation,
child: child,
),
);
}
}dart
class _MyWidgetState extends State<MyWidget>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
);
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
);
_controller.forward();
}
void dispose() {
_controller.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return FadeTransition(
opacity: _animation,
child: ScaleTransition(
scale: _animation,
child: child,
),
);
}
}Hero Animations
Hero动画
dart
// Source page
Hero(
tag: 'image-${item.id}',
child: Image.network(item.imageUrl),
)
// Destination page (same tag)
Hero(
tag: 'image-${item.id}',
child: Image.network(item.imageUrl),
)dart
// 源页面
Hero(
tag: 'image-${item.id}',
child: Image.network(item.imageUrl),
)
// 目标页面(相同tag)
Hero(
tag: 'image-${item.id}',
child: Image.network(item.imageUrl),
)Production Deployment
生产环境部署
Build Commands
构建命令
bash
undefinedbash
undefinedAndroid App Bundle (recommended for Play Store)
Android App Bundle(Play商店推荐)
flutter build appbundle --release --obfuscate --split-debug-info=symbols/
flutter build appbundle --release --obfuscate --split-debug-info=symbols/
Android APK
Android APK
flutter build apk --release --split-per-abi
flutter build apk --release --split-per-abi
iOS
iOS
flutter build ios --release
flutter build ios --release
Web
Web
flutter build web --release
flutter build web --release
Desktop
桌面端
flutter build windows --release
flutter build macos --release
flutter build linux --release
undefinedflutter build windows --release
flutter build macos --release
flutter build linux --release
undefinedCode Obfuscation
代码混淆
Always obfuscate for production:
bash
flutter build appbundle \
--obfuscate \
--split-debug-info=symbols/Store symbol files for crash symbolication.
生产环境务必混淆:
bash
flutter build appbundle \
--obfuscate \
--split-debug-info=symbols/保存符号文件用于崩溃符号解析。
CI/CD Pipeline (GitHub Actions Example)
CI/CD流水线(GitHub Actions示例)
yaml
name: Flutter CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.x'
- run: flutter pub get
- run: flutter analyze
- run: flutter test
- run: flutter build apk --releaseyaml
name: Flutter CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.x'
- run: flutter pub get
- run: flutter analyze
- run: flutter test
- run: flutter build apk --releaseReferences
参考资料
For detailed guides on specific topics:
- State Management Deep Dive: See references/state-management.md
- Testing Patterns: See references/testing.md
- Performance Optimization: See references/performance.md
- Platform Integration: See references/platform-channels.md
如需特定主题的详细指南:
- 状态管理深度解析:参见 references/state-management.md
- 测试模式:参见 references/testing.md
- 性能优化:参见 references/performance.md
- 平台集成:参见 references/platform-channels.md