本文总结了Flutter开发中的编码技巧与最佳实践,帮助开发者提升代码质量和应用性能,无论是初学者还是经验丰富的开发者都能从中受益。这些技巧和规则只是对你的编码提供建议,并不是限制你发挥。

提升 Flutter 代码质量的技巧与实践

提升 Flutter 代码质量的技巧与实践

视频

https://youtu.be/0ZxBj-lG9Z8

https://www.bilibili.com/video/BV1jztoeDEeB/

前言

原文 提升Flutter代码质量的技巧与实践

本文总结了Flutter开发中的编码技巧与最佳实践,帮助开发者提升代码质量和应用性能,无论是初学者还是经验丰富的开发者都能从中受益。

这些技巧和规则只是对你的编码提供建议,并不是限制你发挥。

参考

  1. Flutter 官方文档
  2. Dart 编程语言指南
  3. Flutter Community(Flutter 社区资源)
  4. Stack Overflow Flutter 话题

正文

遵循 DRY 原则

DRY(不要重复自己)是软件开发中的一个基本原则,旨在减少代码的重复。

**示例:

// Instead of
double calculateArea(double width, double height) {
  return width * height;
}
double calculateVolume(double width, double height, double depth) {
  return width * height * depth;
// Use
double calculateArea(double width, double height) {
  return width * height;
}
double calculateVolume(double width, double height, double depth) {
  return calculateArea(width, height) * depth;
}

使用 enum 用于固定常量集

枚举是表示一组固定常量的好方法,可以使你的代码更易读和更易于维护。

示例:

enum Status {
  active,
  inactive,
  pending,
}

// Usage
Status userStatus = Status.active;

避免使用晦涩的缩写

使用描述性名称使代码更易读。

示例:

// Instead of
void calc() {}

// Use
void calculateTotal() {}

使用 StreamBuilder 实现实时数据

StreamBuilder 是一个根据与 Stream 的最新交互快照来构建自身的 widget。

示例:

class RealTimeWidget extends StatelessWidget {
  final Stream<int> _stream = Stream.periodic(Duration(seconds: 1), (count) => count);

@override
  Widget build(BuildContext context) {
    return StreamBuilder<int>(
      stream: _stream,
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return CircularProgressIndicator();
        } else if (snapshot.hasError) {
          return Text('Error: ${snapshot.error}');
        } else {
          return Text('Count: ${snapshot.data}');
        }
      },
    );
  }
}

使用 FutureBuilder 处理异步数据

使用 FutureBuilder 显示异步数据。

示例:

class DataWidget extends StatelessWidget {
  Future<String> fetchData() async {
    await Future.delayed(Duration(seconds: 2));
    return 'Data';
  }

@override
  Widget build(BuildContext context) {
    return FutureBuilder<String>(
      future: fetchData(),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return CircularProgressIndicator();
        } else if (snapshot.hasError) {
          return Text('Error: ${snapshot.error}');
        } else {
          return Text('Data: ${snapshot.data}');
        }
      },
    );
  }
}

根据项目需求使用适当的状态管理

选择最适合您项目需求的状态管理解决方案。

示例:

// For simple state management, use Provider
class Counter with ChangeNotifier {
  int _count = 0;
  int get count => _count;
  void increment() {
    _count++;
    notifyListeners();
  }
  
// For complex state management, use Bloc
class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0);
  @override
  Stream<int> mapEventToState(CounterEvent event) async* {
    if (event is Increment) {
      yield state + 1;
    }
  }
}

使用关键小部件进行高效更新

键帮助 Flutter 识别哪些小部件需要更新。

示例:

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: items.length,
      itemBuilder: (context, index) {
        return ListTile(
          key: ValueKey(items[index].id),
          title: Text(items[index].name),
        );
      },
    );
  }
}

避免嵌套的 try-catch 块

保持错误处理简单以提高可读性。

示例:

try {
  // Code that might throw an exception
} catch (e) {
  // Handle exception
}

将布局与逻辑分离

将 UI 布局与业务逻辑分离,使您的代码更易于维护。

示例:

// Layout
class LoginScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: LoginButton(),
      ),
    );
  }
}

// Logic
class LoginButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () {
        // Handle login
      },
      child: Text('Login'),
    );
  }
}

使用配置文件来管理常量

将常量存储在配置文件或常量类中,以便更方便地管理它们。

示例:

const String API_URL = 'https://api.example.com';

避免使用过于复杂的 Widgets

保持小部件简单和专注。

示例:

// Instead of
class ComplexWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Title'),
        Expanded(
          child: ListView(
            children: [
              ListTile(title: Text('Item 1')),
              ListTile(title: Text('Item 2')),
            ],
          ),
        ),
        ElevatedButton(onPressed: () {}, child: Text('Button')),
      ],
    );
  }
}


// Use
class SimpleWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Title'),
        ListView(
          children: [
            ListTile(title: Text('Item 1')),
            ListTile(title: Text('Item 2')),
          ],
        ),
        ElevatedButton(onPressed: () {}, child: Text('Button')),
      ],
    );
  }
}

对于非变量状态使用 Final

使用 final 用于那些初始化后不应更改的变量。

示例:

final String userName = 'John Doe';

使用 Widgets 用于 UI 组件

将 UI 组件封装成可重用的小部件以促进代码复用。

示例:

class MyButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(onPressed: () {}, child: Text('Press Me'));
  }
}

使用构造函数初始化列表

在类中的构造函数中使用初始化列表以提高性能和可读性。

示例:

class User {
  final String name;
  final int age;
  
// Instead of initializing fields within the constructor body
  User(String name, int age) : name = name, age = age;
}

函数名称应为动词

函数应描述操作以清楚传达它们的功能。

示例:

// Instead of
void nameCheck() {}

// Use
void checkName() {}

避免使用 assert 进行数据验证

使用断言进行调试,而不是在生产环境中进行数据验证。

示例:

// Instead of
assert(user != null, 'User cannot be null');

// Use
if (user == null) {
  throw ArgumentError('User cannot be null');
}

使用 Try-Finally 处理资源

确保使用 try-finally 代码块正确管理资源。

示例:

// Using await and try-finally for resource management
Future<void> readFile() async {
  final file = File('example.txt');
  try {
    final contents = await file.readAsString();
    print(contents);
  } finally {
    // Ensure the file is properly closed or resources are cleaned up
  }
}

使用类型注解

明确标注类型以提高可读性和可维护性。

示例:

// Instead of
var name = 'Flutter';

// Use
String name = 'Flutter';

使用依赖注入

使用依赖注入来促进组件之间的松耦合。

示例:

class AuthService {
  void login(String username, String password) {}
}

class LoginScreen {
  final AuthService _authService;
  LoginScreen(this._authService);
}

保持相关代码行靠近

将相关的代码行放在一起,以便于理解和维护。

示例:

class Order {
  final List<Item> items;

Order(this.items);
  double calculateTotal() {
    return items.fold(0, (total, item) => total + item.price);
  }
}

避免使用魔术数字

硬编码的值可能会令人困惑。使用命名常量以提高清晰度。

示例:

// Instead of
final padding = 16.0;

// Use
const double kPadding = 16.0;
final padding = kPadding;

安全处理空值

使用空感知运算符来安全地处理空值。

示例:

// Instead of
if (user != null) {
  print(user.name);

// Use
print(user?.name);

使用自定义异常处理特定错误

创建自定义异常以实现更好的错误处理。

示例:

class CustomException implements Exception {
  final String message;
  CustomException(this.message);
  @override
  String toString() {
    return 'CustomException: $message';
  }
}

使用描述性名称为变量命名

变量应描述其用途。

示例:

// Instead of
var n = 100;

// Use
var maxUsers = 100;

对于不可变类使用 const 构造器

使用 const 构造器用于不可变的类。

示例:

class Point {
  final double x;
  final double y;

  const Point(this.x, this.y);
}

类应该小

类应该只有一个职责。

示例:

// Instead of
class User {
  String name;
  String address;
  void login() {}
  void logout() {}

// Use
class User {
  String name;
}
class AuthService {
  void login() {}
  void logout() {}
}

对于有状态的 UI,请使用 Stateful Widgets

使用 StatefulWidget 当 UI 需要动态更改时。

示例:

class Counter extends StatefulWidget {
  @override
  _CounterState createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int _count = 0;
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('$_count'),
        ElevatedButton(
          onPressed: () {
            setState(() {
              _count++;
            });
          },
          child: Text('Increment'),
        ),
      ],
    );
  }
}

节制使用 const 关键字用于小部件

使用 const 关键字用于 widgets 有助于 Flutter 通过减少重建和提高 widget 树的效率来优化性能。

示例:

// Instead of
Widget build(BuildContext context) {
  return Text('Hello, World!');
}

// Use
Widget build(BuildContext context) {
  return const Text('Hello, World!');
}

避免过长的方法

将长方法拆分成更小、更易管理的部分。每个方法应该只执行一个任务。

示例:

// Instead of
void processOrder(Order order) {
  validateOrder(order);
  calculateTotal(order);
  updateInventory(order);
  sendConfirmationEmail(order);

// Use
void processOrder(Order order) {
  validateOrder(order);
  calculateTotal(order);
  updateInventory(order);
  sendConfirmationEmail(order);
}
void validateOrder(Order order) {
  // Validation logic
}
void calculateTotal(Order order) {
  // Calculation logic
}
void updateInventory(Order order) {
  // Inventory update logic
}
void sendConfirmationEmail(Order order) {
  // Email sending logic
}

对于复杂的部件,请使用建造者模式

使用构建者模式将复杂对象的构建与其表示分离。

示例:

class ComplexWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        buildHeader(),
        buildContent(),
        buildFooter(),
      ],
    );
  }

Widget buildHeader() {
    return Text('Header');
  }
  Widget buildContent() {
    return Text('Content');
  }
  Widget buildFooter() {
    return Text('Footer');
  }
}

指定参数和返回类型

始终为函数参数和返回类型指定类型。

示例:

// Instead of
void add(a, b) {
  return a + b;
  
// Use
int add(int a, int b) {
  return a + b;
}

避免噪音/冗余词

保持名称简明扼要。

示例:

// Instead of
void getUserName() {}

// Use
void fetchUser() {}

保持函数简短

一个函数应该只做一件事情,这样更便于测试和维护。

示例:

// Instead of
void manageOrder(Order order) {
  if (order.isValid()) {
    order.calculateTotal();
    order.updateInventory();
    order.sendConfirmationEmail();
  }

// Use
void manageOrder(Order order) {
  if (order.isValid()) {
    calculateTotal(order);
    updateInventory(order);
    sendConfirmationEmail(order);
  }
}
void calculateTotal(Order order) {
  // Calculation logic
}
void updateInventory(Order order) {
  // Inventory update logic
}
void sendConfirmationEmail(Order order) {
  // Email sending logic
}

避免过于复杂的状态管理

选择最符合您需求的简单状态管理解决方案。

示例:

// Use Provider for simple state management
class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;
  void increment() {
    _count++;
    notifyListeners();
  }
}

分离 UI 和业务逻辑

保持 UI 和业务逻辑分离以提高可维护性。

示例:

// UI
class CounterScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocBuilder<CounterBloc, int>(
      builder: (context, count) {
        return Text('$count');
      },
    );
  }
}

// Business Logic
class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0);
}

使用 asyncawait 用于异步代码

简化异步代码使用 asyncawait

示例:

Future<void> fetchData() async {
  try {
    final response = await http.get('https://api.example.com/data');
    // Process response
  } catch (e) {
    print('Error: $e');
  }
}

类名应该是名词

类名应该代表实体。

示例:

// Instead of
class DoSomething {}

// Use
class User {}

避免硬编码路径

使用配置文件或环境变量。

示例:

// Instead of
final apiUrl = 'https://api.example.com';

// Use
final apiUrl = dotenv.env['API_URL'];

使用单一职责原则

每个类应该只有一个职责。

示例:

// Instead of
class User {
  String name;
  String address;
  void login() {}
  void logout() {}
// Use
class User {
  String name;
}
class AuthService {
  void login() {}
  void logout() {}
}

避免使用硬编码字符串

使用常量或本地化进行字符串处理。

示例:

// Instead of
final title = 'Welcome';

// Use
const String kTitle = 'Welcome';
final title = kTitle;

对于长列表使用 ListView

使用 ListView 以高效地显示长列表。

示例:

class LongList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: 100,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text('Item $index'),
        );
      },
    );
  }
}

避免使用临时变量

消除不必要的临时变量。

示例:

// Instead of
final tempResult = calculate();
final finalResult = tempResult * 2;

// Use
final finalResult = calculate() * 2;

避免阻塞主线程

将繁重的任务卸载到单独的隔离环境中以防止 UI 冻结。阅读这篇文章。

示例:

Future<void> fetchData() async {
  final data = await compute(fetchDataInBackground, url);
}

Future<String> fetchDataInBackground(String url) {
  // Perform heavy computation
  return data;
}

使用 isis not 进行类型检查

使用 isis not 操作符检查类型。

示例:

// Instead of
if (widget.runtimeType == Text) {
  // Do something

// Use
if (widget is Text) {
  // Do something
}

避免深度嵌套

深层次嵌套的代码难以阅读和维护。

示例:

// Instead of
if (x) {
  if (y) {
    doSomething();
  }

// Use
if (x && y) {
  doSomething();
}

使用 ?? 作为默认值

使用空值感知操作符 ?? 用于默认值。

示例:

String name = userName ?? 'Guest';

通过异常提供上下文

使错误信息具有信息量,以帮助调试。

示例:

try {
  // Code that might throw an exception
} catch (e) {
  print('Error: $e');
}

智慧地使用 setState 方法

最小化 setState 的范围以提高性能。

示例:

// Instead of
setState(() {
  _counter++;
  _someOtherState = true;
});

// Use
setState(() {
  _counter++;
});

遵循既定的代码编写标准

遵循社区认可的编码标准和风格指南。

示例:

// Follow Dart's effective code style
void main() {
  runApp(MyApp());
}

小结

在快速发展的移动应用开发领域,掌握Flutter的编码技巧和最佳实践是提升开发者技能的关键。本文提供了一系列实用的编码法则,旨在帮助Flutter开发者提高代码质量和应用性能。无论是新手还是经验丰富的开发者,通过遵循这些最佳实践,都能在Flutter开发的道路上更进一步,编写出干净、高效且可维护的代码。确保您的应用符合这些编码规范,将为您的开发工作带来显著的提升。

感谢阅读本文

如果有什么建议,请在评论中让我知道。我很乐意改进。


flutter 学习路径


© 猫哥 ducafecat.com

end