Flutter修复Web刷新丢数据后,移动端编译失败(Web端正常)
问题:移除image_picker_web后iOS/Android编译失败,Web端正常运行
修复Web端刷新数据丢失问题时,曾添加image_picker_web依赖,后续移除该依赖后,iOS和Android平台出现编译错误,Web端运行正常。错误出现在final parsed = css.parse(text);处,提示可空类型String?无法赋值给非可空类型Object,导致Gradle的:app:compileFlutterBuildDebug任务执行失败。
错误日志
Launching lib\main.dart on Infinix X6831 in debug mode... main.dart:1 : Error: The argument type 'String?' can't be assigned to the parameter type 'Object' because 'String?' is nullable and 'Object' isn't. element_subclasses.dart:2752 'Object' is from 'dart:core'. final parsed = css.parse(text); ^ Target kernel_snapshot failed: Exception 2 FAILURE: Build failed with an exception. Where: Script 'C:\scr\flutter\packages\flutter_tools\gradle\flutter.gradle' line: 1157 What went wrong: Execution failed for task ':app:compileFlutterBuildDebug'. Process 'command 'C:\scr\flutter\bin\flutter.bat'' finished with non-zero exit value 1 Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. Get more help at https://help.gradle.org BUILD FAILED in 13s Exception: Gradle task assembleDebug failed with exit code 1 Exited (sigterm)
main.dart代码片段
import 'package:auto_route/auto_route.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:kleely/services/exam_service.dart'; import 'package:kleely/services/question_service.dart'; import 'package:overlay_support/overlay_support.dart'; import 'package:provider/provider.dart'; import 'firebase_options.dart'; import 'router/garde.dart'; import 'router/router.gr.dart'; import 'services/chapter_service.dart'; import 'services/class_service.dart'; import 'services/grade.service.dart'; import 'services/sub_chapter_service.dart'; import 'services/subject_service.dart'; import 'services/user_authentication.dart'; import 'translations/codegen_loader.g.dart'; import 'utils/injection/injection_container.dart'; import 'utils/network/internet_checker.dart'; import 'utils/network/no_internet.dart'; import 'utils/theme/app_theme.dart'; import 'utils/theme/presentation/theme_cubit.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await EasyLocalization.ensureInitialized(); SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, ]); await init(); if (kIsWeb) { await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); runApp( OverlaySupport.global( child: EasyLocalization( path: 'assets/translations', supportedLocales: const [Locale('ar')], useFallbackTranslations: true, fallbackLocale: const Locale('ar'), assetLoader: const CodegenLoader(), startLocale: const Locale('ar'), child: MainApp(), ), ), ); } else { bool isConnected = await sl<InternetChecker>().checkInternet(); if (isConnected) { await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); runApp( OverlaySupport.global( child: EasyLocalization( path: 'assets/translations', supportedLocales: const [Locale('ar')], useFallbackTranslations: true, fallbackLocale: const Locale('ar'), assetLoader: const CodegenLoader(), startLocale: const Locale('ar'), child: MainApp(), ), ), ); } else { runApp(const NoInternet()); } } } class MainApp extends StatelessWidget { MainApp({super.key}); final _appRouter = AppRouter(authGuard: AuthGuard()); @override Widget build(BuildContext context) { return MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => UserAuthentication()), Provider(create: (_) => GradeService()), Provider(create: (_) => SubjectService()), Provider(create: (_) => ChapterService()), Provider(create: (_) => SubChapterService()), Provider(create: (_) => ClassService()), Provider(create: (_) => ExamService()), Provider(create: (_) => QuestionService()), BlocProvider<ThemeCubit>(create: (_) { // Initialize ThemeCubit and load the saved theme mode final themeCubit = sl<ThemeCubit>(); themeCubit.loadThemeMode(); return themeCubit; }) ], child: BlocBuilder<ThemeCubit, ThemeMode>( builder: (context, themeMode) { return MaterialApp.router( // easy localization localizationsDelegates: context.localizationDelegates, supportedLocales: context.supportedLocales, locale: context.locale, title: 'Kleely', debugShowCheckedModeBanner: false, // app theme theme: themeMode == ThemeMode.light ? AppTheme.themeData : AppTheme.darkTheme, darkTheme: AppTheme.darkTheme, themeMode: themeMode, routerDelegate: AutoRouterDelegate( _appRouter, navigatorObservers: () => [AutoRouteObserver()], ), routeInformationParser: _appRouter.defaultRouteParser(), builder: (context, router) { ScreenUtil.init( context, designSize: const Size(360, 690), ); return router!; }, ); }, ), ); } }
环境信息
Flutter版本: 3.7.12 Dart版本: 2.19.6 操作系统: Windows 10 Android Studio版本: 2021.3 Visual Studio Code版本: 1.81.1
依赖配置
name: kleely description: A new Flutter project. publish_to: "none" version: 0.1.0 environment: sdk: ">=2.19.6 <3.0.0" dependencies: flutter: sdk: flutter # firebase firebase_core: ^2.15.0 firebase_auth: ^4.7.1 cloud_firestore: ^4.8.3 firebase_storage: ^11.2.5 # other http: ^0.13.6 intl: ^0.17.0 permission_handler: ^10.4.3 dartz: # route path_provider: ^2.1.0 auto_route: ^5.0.2 # Generators username_generator: ^1.3.0 uuid: ^3.0.7 # style flex_color_scheme: ^7.0.5 flutter_carousel_widget: ^2.0.4 flutter_screenutil: ^5.7.0 flutter_spinkit: ^5.2.0 google_fonts: ^4.0.4 shimmer: ^3.0.0 curved_navigation_bar: ^1.0.3 percent_indicator: ^4.2.3 infinite_carousel: ^1.0.2 intl_phone_number_input: ^0.7.3+1 loading_animation_widget: ^1.2.0+4 widget_and_text_animator: ^1.0.0 # localization easy_localization: ^3.0.2 # network and overlay connectivity_plus: ^4.0.2 overlay_support: ^2.1.0 # video and image appinio_video_player: ^1.2.1 image_picker: ^1.0.1 # image_picker_web: ^3.0.0+1 image_cropper: ^1.4.1 # storage hive: ^2.2.3 hive_flutter: ^1.1.0 # state management & dependency injection get: ^4.6.5 get_it: ^7.6.0 bloc: ^8.1.2 flutter_bloc: ^8.1.3 provider: ^6.0.5 image_picker_for_web: ^3.0.0 cached_network_image: ^3.2.3 blurry_modal_progress_hud: ^1.1.1 firebase_performance: ^0.9.2+5 universal_html: ^2.2.2 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.0 auto_route_generator: ^5.0.2 build_runner: ^2.3.3 flutter_launcher_icons: ^0.13.1 flutter_icons: android: true ios: true image_path: "assets/logos/icon.png" flutter: uses-material-design: true assets: - assets/logos/KleeliLogo.png - assets/logos/KleeliIcon.ico - assets/icons/1.png - assets/icons/2.png - assets/icons/3.png - assets/icons/4.png - assets/icons/biology.png - assets/icons/chemistry.png - assets/icons/physics.png - assets/icons/math.png - assets/translations/ - assets/images/c.jpg - assets/audio/loading.mp3
近期修改内容
学生数据存储逻辑调整
Future<Either<Failure, Student>> fetchStudent(String studentId) async { DocumentSnapshot docSnapshot = await _firestore.collection('students').doc(studentId).get(); if (docSnapshot.exists) { final studentData = docSnapshot.data() as Map<String, dynamic>; final student = Student.fromMap(studentData); notifyListeners(); if (kIsWeb) { // Web端将学生数据存入Cookie CookieManager.setObjectAsCookie('student', student.toMap()); } else { // 移动端将学生数据存入Hive await HiveManager.addObjectAsBox('student', student.toMap()); } return right(student); } else { _student = null; notifyListeners(); return left(Failure('No student found with this ID')); } }
图片选择逻辑调整
GestureDetector( onTap: () async { if (kIsWeb) { final ImagePicker _picker = ImagePicker(); XFile? image = await _picker.pickImage(source: ImageSource.gallery); if (image != null) { File file = File(image.path); // ignore: use_build_context_synchronously await Provider.of<UserAuthentication>(context, listen: false) .setProfilePicture(file); } } else { PermissionStatus permissionStatus = await Permission.photos.status; if (permissionStatus.isDenied || permissionStatus.isPermanentlyDenied) { // 可弹出提示告知用户应用需要权限 permissionStatus = await Permission.photos.request(); } if (permissionStatus.isGranted) { final XFile? image = await ImagePicker() .pickImage(source: ImageSource.gallery); if (image != null) { File? croppedFile = await ImageCropper().cropImage( sourcePath: image.path, compressFormat: ImageCompressFormat.jpg, aspectRatioPresets: [ CropAspectRatioPreset.square, CropAspectRatioPreset.ratio3x2, CropAspectRatioPreset.original, CropAspectRatioPreset.ratio4x3, CropAspectRatioPreset.ratio16x9 ], androidUiSettings: AndroidUiSettings( toolbarTitle: 'Cropper', toolbarColor: Colors.white, // ignore: use_build_context_synchronously toolbarWidgetColor: Theme.of(context).primaryColor, initAspectRatio: CropAspectRatioPreset.original, lockAspectRatio: false), iosUiSettings: const IOSUiSettings( title: 'Cropper', ), ); if (croppedFile != null) { // ignore: use_build_context_synchronously await Provider.of<UserAuthentication>(context, listen: false) .setProfilePicture(croppedFile); } } } else { // 处理用户拒绝权限的情况 log("Permission to access gallery is denied"); } } }, child: Consumer<UserAuthentication>( builder: (context, value, child) { return SizedBox( width: 100.w, // 使用screenutil扩展 child: CircleAvatar( radius: 50.r, // 使用screenutil扩展 backgroundImage: NetworkImage( value.user == null ? 'https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50?r=pg' : value.user!.photoURL ?? 'https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50?r=pg', ), ), ); }, ), ),
解决方案建议
清理项目缓存与依赖
- 执行
flutter clean清理Flutter项目缓存 - 执行
flutter pub get重新拉取所有依赖 - Android平台额外执行
gradlew clean清理Gradle缓存
- 执行
修复css.parse的可空类型问题
错误来自css解析逻辑,检查传入css.parse的text变量是否为可空值,添加非空处理:// 方式1:非空断言(确保text不会为空) final parsed = css.parse(text!); // 方式2:空值判断 if (text != null) { final parsed = css.parse(text); }同时检查引入css解析的依赖(如
universal_html)版本是否与当前Flutter/Dart版本兼容,尝试更新到最新兼容版本。排查图片选择依赖冲突
当前依赖中保留了image_picker_for_web,而image_picker本身已支持Web端,可尝试移除image_picker_for_web后重新执行flutter pub get,避免依赖冲突。重新生成自动代码
执行flutter pub run build_runner clean和flutter pub run build_runner build,重新生成路由等自动生成的代码,避免旧代码残留导致的编译问题。
内容的提问来源于stack exchange,提问作者Arevan Shamal
相关产品推荐
相关产品推荐

