此文章为个人学习Flutter的笔记
1. Widget 组合优于复杂继承
谷歌示例
class MyWidget extends StatelessWidget {
const MyWidget({
super.key,
required this.title,
});
final String title;
@override
Widget build(BuildContext context) {
return _buildContent(context);
}
Widget _buildContent(BuildContext context) {
return Container(
padding: const EdgeInsets.all(16.0),
child: Text(title),
);
}
}
Google 的 Flutter 团队避免复杂的继承层级结构,转而青睐组合。他们更喜欢小巧、专注、只做好一件事的 Widget,而不是创建试图解决多个问题的复杂基类。
现实编码示例
// 不创建 BaseCardWidget
class ProductCard extends StatelessWidget {
const ProductCard({
super.key,
required this.product,
});
final Product product;
@override
Widget build(BuildContext context) {
return Card(
child: Column(
children: [
_buildImage(),
_buildTitle(),
_buildPrice(),
],
),
);
}
Widget _buildImage() => Image.network(product.imageUrl);
Widget _buildTitle() => Text(product.name);
Widget _buildPrice() => Text('$${product.price}');
}
2. 状态管理:保持简单,傻瓜式(KISS 原则)
谷歌示例
// Google 的 Flutter 团队这么做
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
@override
State<CounterWidget> createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0;
void _increment() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('$_counter'),
ElevatedButton(
onPressed: _increment,
child: const Text('Increment'),
),
],
);
}
}
谷歌Flutter 团队使用 StatefulWidget 和 setState 的频率远高于任何复杂的状态管理解决方案。他们将高级状态管理方案保留给真正复杂的场景,而不是简单的 UI 状态。
原则: 使用最简单的解决方案。不要过度设计。
3. 方法命名:面向动作,而非面向描述
谷歌示例
class UserService {
Future<User> fetch(String id) async {}
Future<void> update(User user) async {}
Future<void> delete(String id) async {}
}
Google 的 Flutter 团队更喜欢简洁、面向动作的方法命名,在上下文中意义清晰。上下文(UserService,用户服务)已经告诉你这是关于用户的,所以方法名会专注于动作:fetch(获取)、update(更新)、delete(删除)。然而,他们不会为了简洁而牺牲清晰度——当可能出现歧义时,他们仍然会使用描述性名称,例如 fetchUserById() 或 getAuthToken()。
现实编码示例
class CartService {
Future<void> add(Product product) async {}
Future<void> remove(String productId) async {}
Future<void> clear() async {}
Future<double> total() async {}
}
4. Widget 文件结构:一个 Widget,一个目的**
谷歌示例
// user_card.dart
class UserCard extends StatelessWidget {}
// user_list.dart
class UserList extends StatelessWidget {}
// user_profile.dart
class UserProfile extends StatelessWidget {}
// user_settings.dart
class UserSettings extends StatelessWidget {}
Google 的 Flutter 团队遵循严格的一个 Widget 一个文件的规则。每个 Widget 都存在于自己的文件中。 这遵循了单一职责原则:一个文件,一个 Widget,一个目的。它存在于自己的文件中,使得代码库更易于维护和导航。
5. 常量:上下文驱动分组
谷歌示例
// ui_constants.dart
class UiConstants {
static const double defaultPadding = 16.0;
static const double defaultRadius = 8.0;
}
// theme_constants.dart
class ThemeConstants {
static const Color primaryColor = Colors.blue;
static const Color secondaryColor = Colors.grey;
}
// network_constants.dart
class NetworkConstants {
static const int maxRetries = 3;
static const Duration timeout = Duration(seconds: 30);
}
Google 团队根据上下文对常量进行分组,而不是将所有常量都放在一个巨大的文件中。这使得查找和维护相关的常量变得更加容易。
6. 错误处理:明确且及时
谷歌示例
class ApiService {
Future<User> getUser(String id) async {
final response = await _httpClient.get('/users/$id');
if (response.statusCode != 200) {
throw ApiException('Failed to fetch user: ${response.statusCode}');
}
return User.fromJson(response.data);
}
}
Google 的 Flutter 团队更倾向于使用异常(exceptions)进行明确的错误处理,而不是将所有内容都包装在 Result 类型中。他们让错误自然地冒泡,并在适当的级别进行处理。
7. Widget 测试:行为重于实现
谷歌示例
testWidgets('User can increment counter', (tester) async {
await tester.pumpWidget(const MyApp());
// User sees initial counter
expect(find.text('0'), findsOneWidget);
// User taps increment
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// User sees updated counter
expect(find.text('1'), findsOneWidget);
});
Google 的应用级 Widget 测试侧重于用户行为而非实现细节。虽然框架内部仍然需要实现层面的测试(如渲染对象或布局行为),但他们来自 Flutter Gallery 等项目的应用测试更强调用户体验而非代码结构。
总结:
- 保持 Widget 小巧且专注
- 使用最简单的状态管理方案
- 命名清晰简洁
- 一个 Widget 一个文件
- 根据上下文对常量进行分组
- 明确处理错误
- 在应用级测试中测试用户行为