网站做端口是什么,专做立体化的网站,手机app开发需要什么技术,自助模板网站建设做seoFlutter 最佳实践和编码准则 视频 前言 最佳实践是一套既定的准则#xff0c;可以提高代码质量、可读性和可靠性。它们确保遵循行业标准#xff0c;鼓励一致性#xff0c;并促进开发人员之间的合作。通过遵循最佳实践#xff0c;代码变得更容易理解、修改和调试#xff… Flutter 最佳实践和编码准则 视频 前言 最佳实践是一套既定的准则可以提高代码质量、可读性和可靠性。它们确保遵循行业标准鼓励一致性并促进开发人员之间的合作。通过遵循最佳实践代码变得更容易理解、修改和调试从而提高整体软件质量。 原文 https://ducafecat.com/blog/flutter-best-practices-and-coding-guidelines 参考 https://dart.dev/effective-dart/style 正文开始 有许多准则和实践可以采用来提高代码质量和应用性能。 Naming convention 命名规范 类、枚举、类型定义、混入和扩展的名称应使用大驼峰命名法。 # Good class ClassName {}extension ExtensionName on String {}enum EnumName {}mixin MixinName{}typedef FunctionName void Function(); # Badclass Classname {}extension Extensionname on String {}enum Enumname {}mixin Mixinname{}typedef Functionname void Function(); Libraries、包、目录和源文件的名称应该使用蛇形命名法小写字母加下划线。 # Good my_package└─ lib └─ bottom_nav.dart# Bad mypackage└─ lib └─ bottom-nav.dart 导入的前缀命名应该使用蛇形命名法小写字母加下划线。 # Good import package:dio/dio.dart as dio;#Bad import package:dio/dio.dart as Dio; 变量、常量、参数和命名参数应该使用小驼峰命名法。 # Goodint phoneNumber;const pieValue3.14;// parametrsdouble calculateBMI(int weightInKg, int heightInMeter) { return weightInKg / (heightInMeter * heightInMeter);}//named parametrsdouble calculateBMI({int? weightInKg, int? heightInMeter}) { if(weightInKg !null heightInMeter !null){ return weightInKg / (heightInMeter * heightInMeter); }} # Bad int phone_number;const pie_value3.14;// parametrsdouble calculateBMI(int weight_in_kg, int height_in_meter) { return weight_in_kg / (height_in_meter * height_in_meter);}//named parametrsdouble calculateBMI({int? weight_in_kg, int? height_in_meter}) { return weight_in_kg / (height_in_meter * height_in_meter);} 应该遵循适当有意义的命名规范。 # GoodColor backgroundColor;int calculateAge(Date dob);# BadColor bg;int age(Date date); 私有变量名前面加下划线。 class ClassName {// private variableString _variableName;} 使用可空运算符 在处理条件表达式时建议使用 ?? 如果为null和 ?. null aware运算符而不是显式的null检查。 ?? 如果为空运算符 # BadString? name;name namenull ? unknown: name;# GoodString? name;name name ?? unknown; ?. 空值安全运算符 # BadString? name;name namenull? null: name.length.toString();# GoodString? name;namename?.length.toString(); 为了避免潜在的异常情况在Flutter中建议使用 is 运算符而不是 as 强制转换运算符。 is 运算符允许更安全地进行类型检查如果转换不可能也不会抛出异常。 # Bad(person as Person).nameAshish;# Good if(person is Person){ person.nameAshish;} 避免不必要地创建lambda函数 Lambda 函数也称为匿名函数或闭包是一种无需声明函数名称即可定义的函数。它是一种简洁、灵活的函数编写方式通常用于需要传递函数作为参数或以函数作为返回值的语言特性中。 在 Dart 和许多其他编程语言中Lambda 函数可以使用箭头语法或 () {} 语法来定义。例如在 Dart 中下面的代码演示了如何使用箭头语法定义一个 lambda 函数在可以使用 tear-off 的情况下避免不必要地创建 lambda 函数。如果一个函数只是简单地调用一个带有相同参数的方法就没有必要手动将调用包装在 lambda 函数中。 # Badvoid main(){ Listint oddNumber[1,3,4,5,6,7,9,11]; oddNumber.forEach((number){ print(number); });} # Good void main(){ Listint oddNumber[1,3,4,5,6,7,9,11]; oddNumber.forEach(print);} 使用扩展集合简化您的代码 当你已经在另一个集合中存储了现有的项目时利用扩展集合可以简化代码。 # Bad Listint firstFiveOddNumber[1,3,5,7,9]; Listint secondFiveOddNumber[11,13,15,17,19]; firstFiveOddNumber.addAll(secondFiveOddNumber);# Good Listint secondFiveOddNumber[11,13,15,17,19]; Listint firstFiveOddNumber[1,3,5,7,9,...secondFiveOddNumber]; 使用级联操作简化对象操作 Cascades级联操作符非常适合在同一对象上执行一系列操作使代码更加简洁易读。 class Person { String? name; int? age; Person({ this.name, this.age, }); override String toString() { return name: $name age $age; }} # Bad void main(){ final personPerson(); person.nameAshish; person.age25; print(person.toString());}# Good void main(){ final personPerson(); person ..nameAshish ..age25; print(person.toString());} 使用if条件在行和列中实现最佳widget 渲染 在根据行或列中的条件渲染widget 时建议使用if条件而不是可能返回null的条件表达式。 # BadColumn( children: [ isLoggedIn ? ElevatedButton( onPressed: () {}, child: const Text(Go to Login page), ) : const SizedBox(), ], ), # GoodColumn( children: [ if(isLoggedIn) ElevatedButton( onPressed: () {}, child: const Text(Go to Login page), ) ], ), 使用箭头函数 如果一个函数只有一条语句使用 () 箭头函数。 # Bad double calculateBMI(int weight_in_kg, int height_in_meter) { return weight_in_kg / (height_in_meter * height_in_meter);}# Gooddouble calculateBMI(int weight_in_kg, int height_in_meter) weight_in_kg / (height_in_meter * height_in_meter); 删除任何打印语句、未使用的和被注释的代码 在 Flutter 中使用 print 语句来输出调试信息是可行的但不建议在生产环境中使用因为它有几个缺点 输出的信息可能难以区分在 Flutter 应用程序中输出的信息可能会与应用程序本身的输出混杂在一起这可能会导致输出的信息难以区分。 输出的信息可能不可靠 print 语句输出的信息通常会被缓存因此可能不会立即显示出来。这可能会导致在应用程序崩溃之前无法看到最后一次输出的信息。 输出的信息可能会影响应用程序性能在某些情况下输出的信息可能会大量占用应用程序的资源影响应用程序的性能。 因此Flutter 推荐使用专门的日志记录库如 logger 或 flutter_bloc 中的 BlocObserver以便在应用程序中输出可靠、易于区分和可控制的日志。这些库允许您定义输出的日志级别、输出到不同的目标如控制台或文件以及格式化日志消息等。例如使用 logger 库您可以按以下方式输出日志消息 # Bad # production mode// commented message---main method void main(){ print(print statement); //..rest of code}void unusedFunction(){} # Good # production mode void main(){//..rest of code} 正确的文件夹结构 将代码分离到适当的文件夹结构中包括提供者providers、模型models、屏幕/页面screens/pages、服务services、常量constants和工具utils。 project/ lib/ providers/ auth_provider.dart models/ user.dart screens/ home_screen.dart login_screen.dart utils.dart constants.dart services.dart main.dart 代码格式正确适当使用 lints 配置。 include: package:flutter_lints/flutter.yamlanalyzer: errors: require_trailing_commas: errorlinter: rules: require_trailing_commas: true prefer_relative_imports: true 尝试通过在 utils 文件夹中保存的辅助函数中实现代码的可重用性。 # utils.dartimport package:intl/intl.dart;String formatDateTime(DateTime dateTime) { final formatter DateFormat(yyyy-MM-dd HH:mm:ss); return formatter.format(dateTime);} widget 还应该被设计成可重复使用的并可以单独保存在widgets文件夹中。 # text_input.dartimport package:flutter/material.dart;class TextInput extends StatelessWidget { final String? label; final String? hintText; final TextEditingController? controller; final TextInputType keyboardType; final bool obscureText; final String? Function(String?)? validator; final Widget? suffix; const TextInput({ this.label, this.hintText, this.suffix, this.controller, this.validator, this.obscureText false, this.keyboardType TextInputType.text, }); override Widget build(BuildContext context) { return TextFormField( decoration: InputDecoration( labelText: label, hintText:hintText suffixIcon:suffix, ), controller: controller, obscureText: obscureText, validator:validator keyboardType: keyboardType, ); }} 在UI界面中避免使用静态或硬编码的字符串建议根据其范围将其组织在单独的文件夹或文件中。 # Good# validators/ common_validator.dart mixin CommonValidator{ String? emptyValidator(String value) { if (value.isEmpty) { return Please enter; } else { return null; } }}#config/themes colors.dartclass AppColors{static const whiteColor(0xffffffff);static const blackColor(0xff000000);}class LoginPage extends StatelessWidget with CommonValidator { const LoginPage({super.key}); override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: AppColors.black, // good title: const Text(Login page), ), body: Column( children: [ TextInput( label: email, hintText: email address, validator: emptyValidator, // good ) ], ), ); }} #Bad class LoginPage extends StatelessWidget { const LoginPage({super.key}); override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: const Color(0xff000000), // bad title: const Text(Login page), ), body: Column( children: [ TextInput( label: email, hintText: email address, validator: (value) { // bad if (value!.isEmpty) { return Please enter; } else { return null; } }, ) ], ), ); }} widget 组织 将widget 拆分为不同的widget 而不是同一个文件。 在widget 中使用const 当在一个State上调用setState()时所有子孙widget都会重新构建。因此将widget拆分为小的widget这样setState()调用只会重新构建那些实际需要改变UI的子树的部分。 # Badclass LoginPage extends StatefulWidget { const LoginPage({super.key}); override StateLoginPage createState() _LoginPageState();}class _LoginPageState extends StateLoginPage { bool _secureText true; override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text(Login page), ), body: Column( children: [ const TextInput( label: Email, hintText: Email address, ), TextInput( label: Password, hintText: Password, obscureText: _secureText, suffix: IconButton( onPressed: () { setState(() { _secureText !_secureText; }); }, icon: Icon( _secureText ? Icons.visibility_off : Icons.visibility)), ), ElevatedButton( onPressed: () {}, child: const Text(Login)) ], ), ); }} # Goodclass LoginPage extends StatelessWidget { const LoginPage({super.key}); override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text(Login page), ), body: Column( children: [ const TextInput( label: Email, hintText: Email address, ), const TextInput( label: Password, hintText: Password, obscureText: true, ), ElevatedButton( onPressed: () {}, child: const Text(Login)) ], ), ); }}//separate TextFormField Componentclass TextInput extends StatefulWidget { final String? label; final TextEditingController? controller; final String? hintText; final TextInputType keyboardType; final String? Function(String?)? validator; final bool obscureText; const TextInput({ super.key, this.label, this.hintText, this.validator, this.obscureText false, this.controller, this.keyboardType TextInputType.text, }); override StateTextInput createState() _TextInputState();}class _TextInputState extends StateTextInput { bool _secureText false; override void initState() { _secureText widget.obscureText; super.initState(); } override Widget build(BuildContext context) { return TextFormField( decoration: InputDecoration( labelText: widget.label, hintText: widget.hintText, suffixIcon: widget.obscureText ? IconButton( onPressed: () { setState(() { _secureText !_secureText; }); }, icon: Icon( _secureText ? Icons.visibility_off : Icons.visibility, color: Colors.grey, ), ) : null), controller: widget.controller, validator: widget.validator, obscureText: _secureText, keyboardType: widget.keyboardType, ); }} 遵循代码规范 在lib/目录中避免使用相对导入。请使用包导入。 避免使用 print 打印语句 # Badimport widgets/text_input.dart;import widgets/button.dartimport ../widgets/custom_tile.dart;# Goodimport package:coding_guidelines/widgets/text_input.dart;import package:coding_guidelines/widgets/button.dartimport package:coding_guidelines/widgets/custom_tile.dart;# Badvoid f(int x) { print(debug: $x); ...}# Goodvoid f(int x) { debugPrint(debug: $x);}linter: rules: - avoid_empty_else - always_use_package_imports - avoid_print 适当的状态管理 使用Provider作为推荐的状态管理包但是Riverpod与Provider相似可以被视为其改进版本。 您还可以选择使用其他状态管理方法如Bloc、Riverpod、Getx和Redux。 业务逻辑应该与用户界面分离。 # Bad class CounterScreen extends StatefulWidget { const CounterScreen({ super.key, }); override StateCounterScreen createState() _CounterScreenState();}class _CounterScreenState extends StateCounterScreen { int _counter 0; void _incrementCounter() { setState(() { _counter; }); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: const Text(Counter APP), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: Widget[ const Text( You have pushed the button this many times:, ), Text( $_counter, style: Theme.of(context).textTheme.headlineMedium, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: Increment, child: const Icon(Icons.add), ), ); }} # Good// separte logic from UI // provider state managementclass CounterProvider with ChangeNotifier { int _counter 0; int get counter _counter; void incrementCounter() { _counter; notifyListeners(); } void decrementCounter() { _counter--; notifyListeners(); }}// UI class CounterScreen extends StatelessWidget { const CounterScreen({ super.key, }); override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: const Text(Counter APP), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: Widget[ const Text( You have pushed the button this many times:, ), ConsumerCounterProvider( builder: (context, counter, child) { return Text( counter.counter.toString(), style: Theme.of(context).textTheme.headlineMedium, ); }, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () context.readCounterProvider().incrementCounter(), tooltip: Increment, child: const Icon(Icons.add), ), ); }} 升级第三方包 在应用程序中使用的任何第三方包都需要进行验证因为有时它可能会破坏构建或与当前的Flutter版本不同步。特别是在升级Flutter时务必在升级后检查所有插件和第三方包。请确保它们与当前版本兼容。 错误处理和日志记录 使用try-catch块来正确处理代码中的异常和错误。 使用像 pretty_dio_logger 或 dio_logger 这样的日志记录库来记录重要事件或错误。 # Good final dio Dio() ..interceptors.add(PrettyDioLogger( requestHeader: true, requestBody: true, responseBody: true, responseHeader: false, compact: false, ));Futuredynamic fetchNetworkData() async{ try { // Simulating an asynchronous network call final data await dio.get(endpoint); return data; } catch (e, stackTrace) { print(An exception occurred: $e); print(Stack trace: $stackTrace); return e; // Perform additional error handling actions }} # Badfinal dio Dio(); Futuredynamic fetchNetworkData() { dio.get(endpoint).then((data){ return data;)}.catchError((e) { log.error(e); return e; });} Testing 测试 编写单元测试和widget 测试来确保代码的正确性。 使用像 flutter_test 这样的测试框架来编写和运行测试。 追求高代码覆盖率尤其是对于应用程序的关键部分。 # Good// counter app integartion testingvoid main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group(end-to-end test, () { testWidgets(tap on the floating action button, verify counter, (tester) async { app.main(); await tester.pumpAndSettle(); // Verify the counter starts at 0. expect(find.text(0), findsOneWidget); // Finds the floating action button to tap on. final Finder fab find.byTooltip(Increment); // Emulate a tap on the floating action button. await tester.tap(fab); // Trigger a frame. await tester.pumpAndSettle(); // Verify the counter increments by 1. expect(find.text(1), findsOneWidget); }); });} 版本控制和协作 使用像Git这样的版本控制系统来跟踪变更并与其他开发者合作。 遵循Git的最佳实践例如创建有意义的提交信息和分支策略。 The commit type can include the following:feat – a new feature is introduced with the changesfix – a bug fix has occurredchore – changes that do not relate to a fix or feature and dont modify src or test files (for example updating dependencies)refactor – refactored code that neither fixes a bug nor adds a featuredocs – updates to documentation such as a the README or other markdown filesstyle – changes that do not affect the meaning of the code, likely related to code formatting such as white-space, missing semi-colons, and so on.test – including new or correcting previous testsperf – performance improvementsci – continuous integration relatedbuild – changes that affect the build system or external dependenciesrevert – reverts a previous commit# Goodfeat: button componentchore: change login translation# Badfixed bug on login pageChanged button styleempty commit messages 持续集成与交付 建立一个持续集成CI流水线自动运行测试和检查你的代码库。 控制台可以用 CI services like Jenkins, Travis CI, or GitHub Actions. 写一些文档 使用注释来记录你的代码尤其是对于复杂或不明显的部分。 请使用描述性和有意义的注释来解释代码片段的目的、行为或用法。 考虑使用Dartdoc等工具生成API文档。 小结 以上的编码准则可以帮助您提高编码标准增强应用性能并让您更好地理解最佳实践。通过遵循这些准则您可以编写更清晰、更易维护的代码优化应用性能并避免常见的陷阱。 感谢阅读本文 如果我有什么错请在评论中让我知道。我很乐意改进。 © 猫哥 ducafecat.com end 本文由 mdnice 多平台发布