Flutter常见问题以及解决方案

1. 性能问题:卡顿和掉帧

问题表现

  • 复杂UI滚动时卡顿
  • 动画不流畅
  • 页面跳转延迟

解决方法

dart

scss 复制代码
// 1. 使用const构造器
class MyWidget extends StatelessWidget {
  // 错误示例
  Widget build() => Container(color: Colors.red);
  
  // 正确示例 - 使用const
  Widget build() => const SizedBox(width: 50);
}

// 2. 列表优化
ListView.builder(
  itemCount: 1000,
  itemBuilder: (context, index) {
    return ListTile(
      key: ValueKey(index), // 使用Key提高性能
      title: Text('Item $index'),
    );
  },
  // 添加以下优化选项
  addAutomaticKeepAlives: false,
  addRepaintBoundaries: false, // 对简单项可禁用
);

// 3. 使用RepaintBoundary隔离重绘
RepaintBoundary(
  child: ExpensiveWidget(),
);

// 4. 异步加载图片
Image.network(
  'url',
  loadingBuilder: (context, child, progress) {
    return progress == null ? child : CircularProgressIndicator();
  },
);

性能工具

bash

arduino 复制代码
# 性能分析
flutter run --profile
flutter run --trace-skia

# 查看性能面板
flutter run --enable-impeller  # 新的渲染引擎

2. 状态管理问题

问题表现

  • 状态不同步
  • 不必要的重建
  • 内存泄漏

解决方法

dart

scala 复制代码
// 1. 选择合适的方案
// 简单应用:Provider + ChangeNotifier
// 中等应用:Riverpod
// 复杂应用:Bloc/Cubit

// 2. Provider最佳实践
final counterProvider = StateProvider<int>((ref) => 0);

class MyWidget extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    
    return Text('$count');
  }
}

// 3. 避免状态滥用
// 使用StateNotifier替代ChangeNotifier
class CounterNotifier extends StateNotifier<int> {
  CounterNotifier() : super(0);
  
  void increment() => state++;
}

// 4. 使用Equatable避免重复构建
class User extends Equatable {
  final String id;
  final String name;
  
  @override
  List<Object> get props => [id, name];
}

3. 依赖包冲突

问题表现

  • pub get失败
  • 运行时错误
  • 功能异常

解决方法

yaml

yaml 复制代码
# pubspec.yaml配置技巧
dependencies:
  # 1. 使用范围版本而非固定版本
  http: ^1.0.0  # 而不是 1.0.0
  
  # 2. 手动解决冲突
  package_a: ^2.0.0
  package_b: 
    version: ^2.1.0
    # 排除冲突的依赖
    dependency_overrides:
      shared_dependency: ^3.0.0

# 3. 使用any版本(谨慎)
  controversial_package: any

命令行工具

bash

csharp 复制代码
# 1. 分析依赖树
flutter pub deps
flutter pub dependency_overrides

# 2. 升级依赖
flutter pub upgrade --major-versions

# 3. 解决冲突步骤
flutter clean
rm pubspec.lock
flutter pub get

# 4. 查看依赖图表
flutter pub deps --style=tree

4. 平台特定问题

iOS问题解决

bash

lua 复制代码
# iOS构建问题
cd ios
pod install --repo-update
pod update

# 修改Podfile
post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      # 解决bitcode问题
      config.build_settings['ENABLE_BITCODE'] = 'NO'
      
      # 解决架构问题
      config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64'
    end
  end
end

Android问题解决

groovy

arduino 复制代码
// android/app/build.gradle
android {
    defaultConfig {
        // 解决64位支持
        ndk {
            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
        }
        
        // 解决multidex
        multiDexEnabled true
    }
    
    // 解决编译版本
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

平台特定代码

dart

dart 复制代码
import 'dart:io' show Platform;

class PlatformUtils {
  static bool get isIOS => Platform.isIOS;
  static bool get isAndroid => Platform.isAndroid;
  
  // 平台特定的UI
  static Widget getPlatformWidget() {
    if (Platform.isIOS) {
      return CupertinoButton(...);
    } else {
      return MaterialButton(...);
    }
  }
}

5. 构建和发布问题

构建失败解决

bash

bash 复制代码
# 1. 清理缓存
flutter clean
rm -rf ios/Pods
rm -rf ios/.symlinks

# 2. 重置环境
flutter doctor
flutter doctor --android-licenses

# 3. 分析构建日志
flutter build apk --verbose 2>&1 | tee build.log

# 4. 使用特定渠道
flutter channel stable
flutter upgrade

# 5. 检查Flutter环境
which flutter
flutter --version

常见构建配置

yaml

arduino 复制代码
# flutter_app/android/app/build.gradle
android {
    signingConfigs {
        release {
            storeFile file("keystore.jks")
            storePassword System.getenv("KEYSTORE_PASSWORD")
            keyPassword System.getenv("KEY_PASSWORD")
            keyAlias "key_alias"
        }
    }
    
    buildTypes {
        release {
            signingConfig signingConfigs.release
            // 启用代码压缩
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile(
                'proguard-android.txt'),
                'proguard-rules.pro'
        }
    }
}

6. 网络请求和数据处理

网络问题解决

dart

dart 复制代码
// 1. 使用Dio替代HttpClient
final dio = Dio(BaseOptions(
  baseUrl: 'https://api.example.com',
  connectTimeout: Duration(seconds: 5),
  receiveTimeout: Duration(seconds: 3),
));

// 2. 添加拦截器
dio.interceptors.add(InterceptorsWrapper(
  onRequest: (options, handler) {
    // 添加token
    options.headers['Authorization'] = 'Bearer $token';
    return handler.next(options);
  },
  onError: (DioError e, handler) {
    // 错误处理
    if (e.type == DioErrorType.connectTimeout) {
      // 重试逻辑
    }
    return handler.next(e);
  },
));

// 3. 使用retry机制
Future<T> retryRequest<T>(Future<T> Function() request) async {
  for (int i = 0; i < 3; i++) {
    try {
      return await request();
    } catch (e) {
      if (i == 2) rethrow;
      await Future.delayed(Duration(seconds: 1 << i));
    }
  }
  throw Exception('Max retries exceeded');
}

// 4. 离线处理
class NetworkService {
  final Connectivity _connectivity = Connectivity();
  
  Future<bool> get isConnected async {
    final result = await _connectivity.checkConnectivity();
    return result != ConnectivityResult.none;
  }
}

数据处理

dart

dart 复制代码
// 1. 使用freezed生成不可变模型
@freezed
class User with _$User {
  factory User({
    required String id,
    required String name,
    String? email,
  }) = _User;
  
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

// 2. 使用json_serializable
@JsonSerializable()
class Product {
  final String id;
  final String name;
  
  Product({required this.id, required this.name});
  
  factory Product.fromJson(Map<String, dynamic> json) => 
      _$ProductFromJson(json);
  Map<String, dynamic> toJson() => _$ProductToJson(this);
}

// 3. 错误边界处理
class ErrorBoundary extends StatelessWidget {
  final Widget child;
  
  @override
  Widget build(BuildContext context) {
    return ErrorWidget.builder((FlutterErrorDetails details) {
      // 记录错误
      FirebaseCrashlytics.instance.recordError(
        details.exception,
        details.stack,
      );
      
      // 显示友好错误页面
      return ErrorScreen(details.exception);
    });
  }
}

7. 导航和路由问题

问题表现

  • 页面跳转动画卡顿
  • 返回栈管理混乱
  • 参数传递丢失
  • 路由守卫难以实现

解决方法

dart

dart 复制代码
// 1. 使用命名路由并统一管理
class AppRoutes {
  static const home = '/';
  static const detail = '/detail';
  static const settings = '/settings';
  
  static Map<String, WidgetBuilder> get routes {
    return {
      home: (context) => HomePage(),
      detail: (context) => DetailPage(),
      settings: (context) => SettingsPage(),
    };
  }
}

// 2. 安全的参数传递
class DetailPage extends StatelessWidget {
  static const routeName = '/detail';
  
  final String id;
  final String title;
  
  const DetailPage({
    Key? key,
    required this.id,
    required this.title,
  }) : super(key: key);
  
  // 路由参数检查
  static Route<dynamic> route(RouteSettings settings) {
    final args = settings.arguments as Map<String, dynamic>?;
    
    if (args == null || args['id'] == null) {
      return MaterialPageRoute(
        builder: (_) => ErrorPage(message: '缺少必要参数'),
      );
    }
    
    return MaterialPageRoute(
      builder: (_) => DetailPage(
        id: args['id'],
        title: args['title'] ?? '',
      ),
    );
  }
}

// 3. 使用 GoRouter 或 AutoRoute 管理复杂路由
dependencies:
  go_router: ^6.0.0
  auto_route: ^4.0.0

// 4. 导航守卫/中间件
final router = GoRouter(
  routes: [
    GoRoute(
      path: '/',
      builder: (context, state) => HomePage(),
      redirect: (context, state) {
        // 检查登录状态
        final isLoggedIn = authProvider.isLoggedIn;
        if (!isLoggedIn) return '/login';
        return null;
      },
    ),
  ],
  errorBuilder: (context, state) => ErrorPage(
    error: state.error,
  ),
);

8. 国际化 (i18n) 和本地化

问题表现

  • 多语言切换不及时更新
  • 文案硬编码
  • RTL(从右到左)布局问题
  • 日期/数字格式化不一致

解决方法

dart

scala 复制代码
// 1. 使用 intl 包和 ARB 文件
dependencies:
  flutter_localizations:
    sdk: flutter
  intl: ^0.18.0
  flutter_localized_locales: ^2.0.0

// 2. 生成本地化代码
# 在 pubspec.yaml 中添加
flutter:
  generate: true

# 创建 l10n.yaml
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart

// 3. 使用生成的本地化类
MaterialApp(
  localizationsDelegates: const [
    AppLocalizations.delegate,
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
    GlobalCupertinoLocalizations.delegate,
  ],
  supportedLocales: const [
    Locale('en', ''), // English
    Locale('zh', 'CN'), // 简体中文
    Locale('ar', ''), // Arabic (RTL)
  ],
  locale: _currentLocale,
  localeResolutionCallback: (locale, supportedLocales) {
    // 支持的语言回调
    for (var supportedLocale in supportedLocales) {
      if (supportedLocale.languageCode == locale?.languageCode) {
        return supportedLocale;
      }
    }
    return supportedLocales.first;
  },
);

// 4. RTL 布局处理
class RTLWrapper extends StatelessWidget {
  final Widget child;
  
  @override
  Widget build(BuildContext context) {
    final isRTL = Localizations.localeOf(context).languageCode == 'ar';
    
    return Directionality(
      textDirection: isRTL ? TextDirection.rtl : TextDirection.ltr,
      child: child,
    );
  }
}

// 5. 动态切换语言
class LocaleProvider extends ChangeNotifier {
  Locale? _locale;
  
  Locale? get locale => _locale;
  
  void setLocale(Locale locale) {
    if (!_supportedLocales.contains(locale)) return;
    
    _locale = locale;
    notifyListeners();
  }
  
  static const _supportedLocales = [
    Locale('en'),
    Locale('zh'),
    Locale('ar'),
  ];
}

9. 主题和样式管理

问题表现

  • 主题切换性能问题
  • 暗黑模式实现复杂
  • 样式不一致
  • 动态主题支持困难

解决方法

dart

php 复制代码
// 1. 使用 Provider 管理主题
class ThemeProvider extends ChangeNotifier {
  ThemeMode _themeMode = ThemeMode.system;
  
  ThemeMode get themeMode => _themeMode;
  
  void setThemeMode(ThemeMode mode) {
    _themeMode = mode;
    notifyListeners();
  }
}

// 2. 创建自定义主题数据类
class AppTheme {
  static const _fontFamily = 'Roboto';
  
  static ThemeData get lightTheme {
    return ThemeData(
      brightness: Brightness.light,
      primaryColor: Colors.blue,
      fontFamily: _fontFamily,
      textTheme: TextTheme(
        headline1: TextStyle(fontSize: 32, fontWeight: FontWeight.bold),
        bodyText1: TextStyle(fontSize: 16),
      ),
      colorScheme: ColorScheme.light(
        primary: Colors.blue,
        secondary: Colors.orange,
      ),
    );
  }
  
  static ThemeData get darkTheme {
    return ThemeData(
      brightness: Brightness.dark,
      primaryColor: Colors.blueGrey,
      fontFamily: _fontFamily,
      colorScheme: ColorScheme.dark(
        primary: Colors.blueGrey,
        secondary: Colors.orange,
      ),
    );
  }
}

// 3. 响应系统主题变化
WidgetsBinding.instance.platformDispatcher.onPlatformBrightnessChanged = () {
  final brightness = WidgetsBinding.instance.platformDispatcher.platformBrightness;
  // 处理主题变化
};

// 4. 使用 Extension 简化样式访问
extension ThemeExtension on BuildContext {
  Color get primaryColor => Theme.of(this).primaryColor;
  TextTheme get textTheme => Theme.of(this).textTheme;
  double get paddingMedium => 16.0;
}

// 使用
Text(
  'Hello',
  style: context.textTheme.headline1,
  color: context.primaryColor,
);

10. 文件存储和缓存

问题表现

  • 文件读写权限问题
  • 缓存清理困难
  • 大文件处理效率低
  • 跨平台路径差异

解决方法

dart

dart 复制代码
// 1. 使用 path_provider 获取路径
import 'package:path_provider/path_provider.dart';

class StorageService {
  Future<String> get _localPath async {
    final directory = await getApplicationDocumentsDirectory();
    return directory.path;
  }
  
  Future<File> get _localFile(String filename) async {
    final path = await _localPath;
    return File('$path/$filename');
  }
  
  Future<void> writeData(String data, String filename) async {
    final file = await _localFile(filename);
    await file.writeAsString(data);
  }
}

// 2. 使用 shared_preferences 存储简单数据
final prefs = await SharedPreferences.getInstance();
await prefs.setString('token', 'jwt_token');
final token = prefs.getString('token');

// 3. 使用 hive 或 sqflite 存储结构化数据
dependencies:
  hive: ^2.2.0
  hive_flutter: ^1.1.0
  sqflite: ^2.2.0

// 4. 图片缓存管理
CachedNetworkImage(
  imageUrl: 'https://example.com/image.jpg',
  placeholder: (context, url) => CircularProgressIndicator(),
  errorWidget: (context, url, error) => Icon(Icons.error),
  cacheManager: DefaultCacheManager(),
  maxHeight: 200,
  maxWidth: 200,
);

// 5. 自定义缓存策略
class SmartCacheManager {
  final CacheManager _cacheManager = CacheManager(
    Config(
      'custom_cache_key',
      maxNrOfCacheObjects: 100,
      stalePeriod: Duration(days: 7),
    ),
  );
  
  Future<void> clearOldCache() async {
    await _cacheManager.emptyCache();
  }
  
  Future<void> clearExpired() async {
    await _cacheManager.removeStale();
  }
}

11. 权限管理

问题表现

  • 权限申请被拒绝
  • 动态权限处理复杂
  • 不同平台权限差异
  • 权限状态管理困难

解决方法

dart

dart 复制代码
// 1. 使用 permission_handler
dependencies:
  permission_handler: ^10.0.0

// 2. 权限服务封装
class PermissionService {
  static Future<bool> requestCameraPermission() async {
    final status = await Permission.camera.request();
    
    if (status.isGranted) {
      return true;
    } else if (status.isDenied) {
      // 可再次请求
      return false;
    } else if (status.isPermanentlyDenied) {
      // 需要引导用户去设置
      await openAppSettings();
      return false;
    }
    return false;
  }
  
  // 3. 批量请求权限
  static Future<Map<Permission, PermissionStatus>> requestMultiple(
    List<Permission> permissions,
  ) async {
    return await permissions.request();
  }
  
  // 4. 检查权限状态
  static Future<bool> checkPermission(Permission permission) async {
    final status = await permission.status;
    return status.isGranted;
  }
}

// 5. 平台特定的权限处理
Future<void> requestIOSPermissions() async {
  if (Platform.isIOS) {
    // iOS 特定权限逻辑
    final status = await Permission.photos.request();
    if (!status.isGranted) {
      // 处理拒绝逻辑
    }
  }
}

// 6. 权限请求 UI 封装
class PermissionRequestDialog extends StatelessWidget {
  final String title;
  final String message;
  final VoidCallback onGranted;
  
  @override
  Widget build(BuildContext context) {
    return AlertDialog(
      title: Text(title),
      content: Text(message),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: Text('取消'),
        ),
        ElevatedButton(
          onPressed: () {
            onGranted();
            Navigator.pop(context);
          },
          child: Text('去设置'),
        ),
      ],
    );
  }
}

12. 混合开发与平台交互

问题表现

  • 平台通道调用失败
  • 原生与 Flutter 通信延迟
  • 插件兼容性问题
  • 二进制大小增加

解决方法

dart

dart 复制代码
// 1. 方法通道封装
class NativeBridge {
  static const platform = MethodChannel('com.example/native');
  
  static Future<String?> callNativeMethod(String method, [dynamic args]) async {
    try {
      final result = await platform.invokeMethod(method, args);
      return result.toString();
    } on PlatformException catch (e) {
      debugPrint('Native call failed: ${e.message}');
      return null;
    }
  }
  
  // 2. 双向通信
  static void setupEventChannel() {
    const eventChannel = EventChannel('com.example/events');
    
    eventChannel.receiveBroadcastStream().listen(
      (event) {
        // 处理来自原生的事件
        _handleNativeEvent(event);
      },
      onError: (error) {
        debugPrint('Event channel error: $error');
      },
    );
  }
}

// 3. 使用 Pigeon 生成类型安全的代码
// schema.dart
import 'package:pigeon/pigeon.dart';

class SearchRequest {
  String? query;
}

class SearchReply {
  String? result;
}

@HostApi()
abstract class SearchApi {
  SearchReply search(SearchRequest request);
}

// 4. 平台特定代码抽象
abstract class PlatformSpecificFeature {
  Future<void> doSomething();
  
  factory PlatformSpecificFeature() {
    if (Platform.isAndroid) {
      return AndroidImplementation();
    } else if (Platform.isIOS) {
      return IOSImplementation();
    }
    throw UnsupportedError('Platform not supported');
  }
}

// 5. 插件开发注意事项
class MyPlugin {
  static void registerWith(Registrar registrar) {
    final channel = MethodChannel(
      'my_plugin',
      StandardMethodCodec(),
      registrar.messenger(),
    );
    
    final instance = MyPlugin();
    channel.setMethodCallHandler(instance.handleMethodCall);
  }
  
  Future<dynamic> handleMethodCall(MethodCall call) async {
    switch (call.method) {
      case 'getPlatformVersion':
        return Platform.version;
      default:
        throw PlatformException(
          code: 'Unimplemented',
          message: 'Method not implemented',
        );
    }
  }
}

13. 调试和日志系统

问题表现

  • 生产环境日志泄露
  • 调试信息不足
  • 性能监控缺失
  • 错误追踪困难

解决方法

dart

scss 复制代码
// 1. 分级日志系统
enum LogLevel { debug, info, warning, error }

class Logger {
  static final Logger _instance = Logger._internal();
  factory Logger() => _instance;
  
  Logger._internal();
  
  void log(LogLevel level, String message, {Object? error, StackTrace? stackTrace}) {
    if (kDebugMode) {
      // 开发环境:输出到控制台
      _printLog(level, message, error, stackTrace);
    }
    
    // 生产环境:发送到服务器
    if (level.index >= LogLevel.warning.index) {
      _sendToServer(level, message, error, stackTrace);
    }
  }
  
  void _printLog(LogLevel level, String message, Object? error, StackTrace? stackTrace) {
    final prefix = '[${level.name.toUpperCase()}]';
    debugPrint('$prefix $message');
    
    if (error != null) {
      debugPrint('Error: $error');
    }
    if (stackTrace != null) {
      debugPrint('Stack: $stackTrace');
    }
  }
}

// 2. 使用 logger 包
dependencies:
  logger: ^1.1.0

final logger = Logger(
  printer: PrettyPrinter(
    methodCount: 2,
    errorMethodCount: 8,
    colors: true,
  ),
);

// 3. 性能监控
void main() {
  // 记录启动时间
  final stopwatch = Stopwatch()..start();
  
  runApp(MyApp());
  
  WidgetsBinding.instance.addPostFrameCallback((_) {
    stopwatch.stop();
    logger.d('App started in ${stopwatch.elapsedMilliseconds}ms');
  });
}

// 4. 网络请求拦截日志
dio.interceptors.add(LogInterceptor(
  request: true,
  requestHeader: true,
  requestBody: true,
  responseHeader: true,
  responseBody: true,
  logPrint: (object) {
    logger.d(object);
  },
));

// 5. 自定义错误页面
class ErrorBoundary extends StatelessWidget {
  final Widget child;
  
  @override
  Widget build(BuildContext context) {
    return ErrorWidget.builder((FlutterErrorDetails details) {
      // 记录错误
      logger.e(details.exceptionAsString(), 
        error: details.exception,
        stackTrace: details.stack,
      );
      
      // 显示用户友好的错误页面
      return ErrorScreen(
        onRetry: () => Navigator.of(context).pop(),
      );
    });
  }
}

14. 测试相关挑战

问题表现

  • 单元测试难以编写
  • Widget 测试缓慢
  • 集成测试不稳定
  • Mock 数据复杂

解决方法

dart

less 复制代码
// 1. 使用 Mockito 和 Build Runner
dev_dependencies:
  mockito: ^5.3.0
  build_runner: ^2.3.0

// 生成 Mock 类
@GenerateMocks([HttpClient])
import 'my_test.mocks.dart';

void main() {
  late MockHttpClient mockClient;
  
  setUp(() {
    mockClient = MockHttpClient();
  });
  
  test('测试网络请求', () async {
    when(mockClient.get(any)).thenAnswer(
      (_) async => HttpResponse(data: 'test', statusCode: 200),
    );
    
    final service = MyService(mockClient);
    final result = await service.fetchData();
    
    expect(result, 'test');
    verify(mockClient.get(any)).called(1);
  });
}

// 2. Widget 测试优化
testWidgets('测试登录页面', (WidgetTester tester) async {
  // 使用 Key 查找组件
  await tester.pumpWidget(MyApp());
  
  // 输入文本
  await tester.enterText(find.byKey(Key('email_field')), 'test@example.com');
  await tester.enterText(find.byKey(Key('password_field')), 'password');
  
  // 点击按钮
  await tester.tap(find.byKey(Key('login_button')));
  
  // 等待异步操作完成
  await tester.pumpAndSettle();
  
  // 验证结果
  expect(find.text('登录成功'), findsOneWidget);
});

// 3. 集成测试最佳实践
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';

void main() {
  IntegrationTestWidgetsFlutterBinding.ensureInitialized();
  
  testWidgets('完整用户流程测试', (WidgetTester tester) async {
    // 启动应用
    await tester.pumpWidget(MyApp());
    
    // 执行用户操作序列
    await _performLogin(tester);
    await _navigateToProfile(tester);
    await _updateProfile(tester);
    
    // 验证最终状态
    expect(find.text('更新成功'), findsOneWidget);
  });
  
  // 4. Golden Tests(视觉回归测试)
  testWidgets('验证UI一致性', (WidgetTester tester) async {
    await tester.pumpWidget(MyWidget());
    
    await expectLater(
      find.byType(MyWidget),
      matchesGoldenFile('goldens/my_widget.png'),
    );
  });
}

15. 持续集成/持续部署 (CI/CD)

问题表现

  • 构建配置复杂
  • 多环境管理困难
  • 自动化测试集成
  • 发布流程繁琐

解决方法

yaml

yaml 复制代码
# .github/workflows/flutter.yml
name: Flutter CI/CD

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v2
    
    - name: Setup Flutter
      uses: subosito/flutter-action@v2
      with:
        flutter-version: '3.0.0'
    
    - name: Install dependencies
      run: flutter pub get
    
    - name: Analyze code
      run: flutter analyze
    
    - name: Run tests
      run: flutter test --coverage
    
    - name: Upload coverage
      uses: codecov/codecov-action@v2
    
  build-android:
    runs-on: ubuntu-latest
    needs: test
    if: github.ref == 'refs/heads/main'
    
    steps:
    - uses: actions/checkout@v2
    
    - name: Setup Java
      uses: actions/setup-java@v2
      with:
        distribution: 'temurin'
        java-version: '11'
    
    - name: Setup Flutter
      uses: subosito/flutter-action@v2
    
    - name: Build APK
      run: |
        flutter build apk --release \
          --dart-define=APP_ENV=production \
          --dart-define=API_URL=https://api.example.com
    
    - name: Upload artifacts
      uses: actions/upload-artifact@v2
      with:
        name: app-release
        path: build/app/outputs/flutter-apk/app-release.apk

环境配置管理

dart

dart 复制代码
// lib/config/environment.dart
abstract class Environment {
  static const String apiUrl = String.fromEnvironment('API_URL');
  static const String appEnv = String.fromEnvironment('APP_ENV');
  
  static bool get isProduction => appEnv == 'production';
  static bool get isDevelopment => appEnv == 'development';
  
  static Map<String, String> get variables {
    return {
      'API_URL': apiUrl,
      'APP_ENV': appEnv,
    };
  }
}

// 构建命令
flutter run --dart-define=APP_ENV=development --dart-define=API_URL=http://localhost:3000

flutter build apk --release --dart-define=APP_ENV=production --dart-define=API_URL=https://api.example.com

16. 其他实用技巧

调试技巧

dart

scss 复制代码
// 1. 调试Build次数
class BuildCounter extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    debugPrint('BuildCounter rebuilt');
    return Container();
  }
}

// 2. 使用DevTools
// 运行:flutter pub global run devtools
// 然后:flutter run --observatory-port=9200

// 3. 性能监控
void main() {
  WidgetsFlutterBinding.ensureInitialized();
  
  // 添加性能监控
  FlutterError.onError = (FlutterErrorDetails details) {
    Zone.current.handleUncaughtError(details.exception, details.stack!);
  };
  
  runZonedGuarded(() {
    runApp(MyApp());
  }, (error, stackTrace) {
    // 记录未捕获异常
    FirebaseCrashlytics.instance.recordError(error, stackTrace);
  });
}

资源优化

yaml

yaml 复制代码
# pubspec.yaml
flutter:
  assets:
    - assets/images/
    
  # 字体优化
  fonts:
    - family: Roboto
      fonts:
        - asset: assets/fonts/Roboto-Regular.ttf
          weight: 400
        - asset: assets/fonts/Roboto-Bold.ttf
          weight: 700
  
  # Shader预编译(减少卡顿)
  shaders:
    - shaders/myshader.frag
相关推荐
无知的前端2 小时前
一文精通-Mixin特性
flutter·面试·dart
BD_Marathon2 小时前
Vue3_Vite构建工程化前端项目
前端
武清伯MVP2 小时前
CSS Grid布局全解析:从基础到实战的二维布局方案
前端·css·grid
xfq2 小时前
typescript泛型枚举以及NaN传染处理
前端·typescript
ErMao2 小时前
开始搭建第一个React项目吧~
前端·react.js
Yanni4Night2 小时前
JavaScript打包器大奖赛:谁是构建速度之王? 🚀
前端·javascript
_大学牲2 小时前
Flutter 勇闯2D像素游戏之路(四):与哥布林战斗的滑步魔法师
flutter·游戏·游戏开发
默海笑3 小时前
VUE后台管理系统:项目架构之搭建Layout架构解决方案与实现
前端·javascript·vue.js
咸鱼加辣3 小时前
【前端脚手架】node
前端