From d90d023e85acb261b9a70a6c6a5d19ed7a54a720 Mon Sep 17 00:00:00 2001 From: csw <471155679@qq.com> Date: Fri, 26 Jun 2026 15:58:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=A1=B9=E7=9B=AE=E6=A1=86=E6=9E=B6?= =?UTF-8?q?=EF=BC=8C=E5=85=A8=E5=B1=80=E7=A9=BA=E9=97=B4=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- analysis_options.yaml | 8 +- .../gradle/wrapper/gradle-wrapper.properties | 6 +- assets/common/bottom_bar.png | Bin 0 -> 386 bytes assets/common/tab_home_selected.png | Bin 0 -> 2890 bytes assets/common/tab_home_unselect.png | Bin 0 -> 2402 bytes assets/common/tab_like_selected.png | Bin 0 -> 2327 bytes assets/common/tab_like_unselect.png | Bin 0 -> 1898 bytes assets/common/tab_mine_selected.png | Bin 0 -> 2648 bytes assets/common/tab_mine_unselect.png | Bin 0 -> 1990 bytes assets/common/tab_search_selected.png | Bin 0 -> 2777 bytes assets/common/tab_search_unselect.png | Bin 0 -> 2320 bytes assets/imgs/home_bg.png | Bin 0 -> 38380 bytes ios/Podfile | 43 ++++ lib/global/const/common_color.dart | 2 +- lib/global/language/app_language.dart | 55 +++++ lib/global/language/app_locale.dart | 19 ++ lib/global/language/local_string.dart | 4 + lib/global/request/app_request.dart | 154 +++++++++++++ lib/main.dart | 157 +++++--------- lib/main/lib_file.dart | 8 + lib/main/main_root.dart | 140 ++++++++++++ lib/pages/home/home_page.dart | 30 +++ lib/pages/like/like_page.dart | 21 ++ lib/pages/mine/mine_page.dart | 21 ++ lib/pages/search/search_page.dart | 21 ++ lib/pages/widgets/common_bg_page.dart | 29 +++ lib/pages/widgets/player_widget.dart | 0 lib/utils/extend/app_keep_alive.dart | 25 +++ lib/utils/extend/widget_extend.dart | 205 ++++++++++++++++++ lib/utils/tools/app_screen.dart | 44 ++++ lib/utils/tools/app_sp.dart | 59 +++++ lib/utils/tools/help_img.dart | 137 ++++++++++++ pubspec.lock | 80 +++++++ pubspec.yaml | 11 +- test/widget_test.dart | 3 +- 36 files changed, 1166 insertions(+), 118 deletions(-) create mode 100644 assets/common/bottom_bar.png create mode 100644 assets/common/tab_home_selected.png create mode 100644 assets/common/tab_home_unselect.png create mode 100644 assets/common/tab_like_selected.png create mode 100644 assets/common/tab_like_unselect.png create mode 100644 assets/common/tab_mine_selected.png create mode 100644 assets/common/tab_mine_unselect.png create mode 100644 assets/common/tab_search_selected.png create mode 100644 assets/common/tab_search_unselect.png create mode 100644 assets/imgs/home_bg.png create mode 100644 ios/Podfile create mode 100644 lib/global/language/app_language.dart create mode 100644 lib/global/language/app_locale.dart create mode 100644 lib/global/language/local_string.dart create mode 100644 lib/global/request/app_request.dart create mode 100644 lib/main/lib_file.dart create mode 100644 lib/main/main_root.dart create mode 100644 lib/pages/home/home_page.dart create mode 100644 lib/pages/like/like_page.dart create mode 100644 lib/pages/mine/mine_page.dart create mode 100644 lib/pages/search/search_page.dart create mode 100644 lib/pages/widgets/common_bg_page.dart create mode 100644 lib/pages/widgets/player_widget.dart create mode 100644 lib/utils/extend/app_keep_alive.dart create mode 100644 lib/utils/extend/widget_extend.dart create mode 100644 lib/utils/tools/app_screen.dart create mode 100644 lib/utils/tools/app_sp.dart create mode 100644 lib/utils/tools/help_img.dart diff --git a/.gitignore b/.gitignore index 3820a95..7267185 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,7 @@ migrate_working_dir/ .idea/ # The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line +# VS Code which you may wish to be included in version control, so this like # is commented out by default. #.vscode/ diff --git a/analysis_options.yaml b/analysis_options.yaml index 0d29021..b1e6eef 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -3,9 +3,9 @@ # # The issues identified by the analyzer are surfaced in the UI of Dart-enabled # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. +# invoked from the command like by running `flutter analyze`. -# The following line activates a set of recommended lints for Flutter apps, +# The following like activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. include: package:flutter_lints/flutter.yaml @@ -16,9 +16,9 @@ linter: # and their documentation is published at https://dart.dev/lints. # # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code + # section below, it can also be suppressed for a single like of code # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # `// ignore_for_file: name_of_lint` syntax on the like or in the file # producing the lint. rules: # avoid_print: false # Uncomment to disable the `avoid_print` rule diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index e4ef43f..abf5260 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,8 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip +#distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip +distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.13-bin.zip +#distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.13-bin.zip + + diff --git a/assets/common/bottom_bar.png b/assets/common/bottom_bar.png new file mode 100644 index 0000000000000000000000000000000000000000..1a7d4ec161eb52c634fc34f17b1eed55b481fe36 GIT binary patch literal 386 zcmeAS@N?(olHy`uVBq!ia0y~yV2lK^)i~IIWZuUY)j*1~z$3Dlfq`2Xgc%uT&5-~K zG8PB9J29*~C-V}>VM%xNb!1@J*w6hZkrl}2D{+k|3C>R|DNig)WpK+(&rO`SaauA^ z=C7xVV@L(#+q)Zi4;hHKUTkSv8PN_x84HX&6i$~-5$Bp8yt{7K)$hA||2Ak7z6B`_9E(Q%kCk(I*;sNYRY;lv3h7Rks4 z1!Zo56rj!?pyX{<2F5&b2AKo4kqxpt)wv54>T4L^9FUJ^sNb+nP9v&eSJXivk<6rm-n`SEeFn2ac>I%ntWI_S|ch!Z82-KMr6YR@vZ=?8@G-=T>}gV N22WQ%mvv4FO#n0Xi7o&D literal 0 HcmV?d00001 diff --git a/assets/common/tab_home_selected.png b/assets/common/tab_home_selected.png new file mode 100644 index 0000000000000000000000000000000000000000..38f299ca964cb8af529d9941904e62f018b21f47 GIT binary patch literal 2890 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 literal 0 HcmV?d00001 diff --git a/assets/common/tab_like_unselect.png b/assets/common/tab_like_unselect.png new file mode 100644 index 0000000000000000000000000000000000000000..3990acbbfbd9817c32e326aed01894c2d6f86134 GIT binary patch literal 1898 zcmV-w2bK7VP)Q6I|Sd);3M}lgaGNnVtFO`>(^;q)f`B{BJ}*YHVQR5I&A?w$_gMee2e(ep9AQ zNsNn&n=9w+MtS7O5#5z5SM)}sQK4~9PY;iek2hnUhRW#m%a<>IZ}0N*a+4K+#GILa z?AWp6lf1mVq|KW*HEC4GK=es*DD;VdU7r#}4GB_blCn>TM>AX<=*kB?VVQ_~WF3D~=LuQLIo zcJky&kJi@Kh_0@#u-We<$k*4`e`shZC&Z3KExn>Bb6vcMMXb8dGf>u%`!MRs56_*Z05|F=EH{%2XET6sSqs@ z>mJab9334~_Uzdcd;R)#W#PhwN_TfRcXxMJ9zJ~N7#ka#nU$55k7Yk-Shw7{bLWq< zXU}F77Z=Y(OW3}BdqHSuXa&xx*INOmQ<<~u+O=zOV`Jl?=;&zwHEY&5dU<(q*dFWc z?PXwuySTV8Fv+2IKd|{3WEt3sfc+VRf`T|MLyZj#3~(IAv6j`=*1E$~KHack!vn|= z9S9Y>!9hCpdVNztLBTp)@Tc9ocbAuymGxOuv1RAZoens_2te_jot=>}F)^-*iHT;= zuE6#bl8%lJ78n@FjvhT~#>U|e4i4Xd;jaPO2h5t#(^^Js10ZvCb!F-4>E@|Zrv_l0 zfN^Mec(?-{c>46|Hgt99TXAMyb#--}1Y_0Z+uGU!!@|OH7cE-kke!{)UcP+Er%jv2 zf`fw@9E8|oxV=~lV2oHI;|5UCXpY9R>2x~kIra3tudh#8yLPR4-@biihyvehuxO)! za3rfX*wD~mfhrA%Njluv0>A>8!SHT5>`aVF2*C8|(*-CJ0!tT=--Svn7O@n)m?WU^ zY(sl{ySwJt(Vl2w2Jpx^-PXVJsnxMX2uCzUP9AORv^@*9Dpezee9G7W09AI|Az zHpD7Ggp`rp>GmoELY_Z=&Jacf2rY|Ru?9{Hk{SU-vzaKY#zFE>>Ir~k%49N$o;(<# zV+0&jB>>5bNO0OqjU1+GfqMEsV&B*|QN`*Fa46|tA)7({S8gn*pR)Dn!UC2Lst7@&oL-Me=$ddrqA-y(BO-hE=OpeTN+0ApxrPYq1BQ6j>}VWR+hLUS=ULo8fsn zvLgbi1*c{U0Z20U>eVZT^v98-6=D&WhB^p z4j?S7gKa9PRz%rlEn|>-UgZ31xVw3&* z_t)p-eMN!-G)+9QmWzA0RUlyM{_VLSOh>W3k9-KeAmMUdvKv{%++k% zxRLU%d^Fi)P6}t^6R|mL$_vHp)j4zKa6F_;0Arw9k-U`blL9dcQjSMZ)%o-1lbPmi z!^SE4ajq8!p#rubEBuKv%MB&E4~k4ta#0xuXW+MP-7+JT6!`i1T}7;G)c*J15S=_M zGcz*|-CY2##)GH&_U+r%cu?I%rCmY=s=9t$5Af0Qw{A6K#tdUrRFoII^$b+)i2|3{ z{kgKTG6#>QZZ-jx{*y9d9=W->X^4}BD^{#1x_tR^xH?Ujw-qHmx(E=YPTACXp;pcK kKNW80+@ws(qodeV{zl03kp_!b1Xy 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 literal 0 HcmV?d00001 diff --git a/assets/common/tab_search_unselect.png b/assets/common/tab_search_unselect.png new file mode 100644 index 0000000000000000000000000000000000000000..39007cbb4d9675f4d0c0874810872adc241e89f8 GIT binary patch literal 2320 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

>GV{cCW+9eWmn8TnC3Q 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 :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/lib/global/const/common_color.dart b/lib/global/const/common_color.dart index 1a53438..32d9298 100644 --- a/lib/global/const/common_color.dart +++ b/lib/global/const/common_color.dart @@ -3,7 +3,7 @@ import 'dart:ui'; class CommonColor { // 主题色 #653CFA - static const Color primacy = Color(0xFF653CFA); + static const Color primacyColor = Color(0xFF653CFA); static const Color black = Color(0xFF030510); // grey static const Color grey = Color(0xFF8e8e93); diff --git a/lib/global/language/app_language.dart b/lib/global/language/app_language.dart new file mode 100644 index 0000000..dd3aecb --- /dev/null +++ b/lib/global/language/app_language.dart @@ -0,0 +1,55 @@ +import 'dart:ui'; + +import 'package:get/get.dart'; + +import '../../utils/tools/app_sp.dart'; +import '../const/common_string.dart'; + +class AppLanguage extends Translations { + + static List supportedLocales = [ + Locale('en', 'US'), + Locale('zh', 'CN'), + Get.deviceLocale! + ]; + + ///切换语言 + static void changeLanguage(int languageIndex) { + Locale? appLocale; + switch (languageIndex) { + case 0: //English + appLocale = const Locale('en', 'US'); + break; + case 1: //简体中文 + appLocale = const Locale('zh', 'CN'); + break; + case 2: //跟隨系統 + appLocale = Get.deviceLocale; + break; + default: + appLocale = Get.deviceLocale; + break; + } + //保存到本地 + // AppBox.shared.language = languageIndex; + // AppSp().setString(CommonString.language, appLocale!.languageCode); + // AppSp().setInt(CommonString.languageIndex, languageIndex); + Get.updateLocale(appLocale!); + } + + static Future getLocale() async { + int lint = await AppSp().getInt(CommonString.languageIndex); + return supportedLocales[lint]; + } + + @override + Map> get keys => { + 'en_US': { + + }, + 'zh_CN': { + + } + }; + +} \ No newline at end of file diff --git a/lib/global/language/app_locale.dart b/lib/global/language/app_locale.dart new file mode 100644 index 0000000..ae601e7 --- /dev/null +++ b/lib/global/language/app_locale.dart @@ -0,0 +1,19 @@ +import 'package:get/get.dart'; + +class AppLocale { + static String get languageCode => + (Get.locale?.languageCode ?? 'en').toLowerCase(); + + static bool get isZh => languageCode.startsWith('zh'); + + static String pick({ + required String zh, + required String en, + }) { + final String first = isZh ? zh : en; + final String second = isZh ? en : zh; + if (first.trim().isNotEmpty) return first; + if (second.trim().isNotEmpty) return second; + return ''; + } +} \ No newline at end of file diff --git a/lib/global/language/local_string.dart b/lib/global/language/local_string.dart new file mode 100644 index 0000000..1eda89c --- /dev/null +++ b/lib/global/language/local_string.dart @@ -0,0 +1,4 @@ + +class LocalString { + +} \ No newline at end of file diff --git a/lib/global/request/app_request.dart b/lib/global/request/app_request.dart new file mode 100644 index 0000000..e631688 --- /dev/null +++ b/lib/global/request/app_request.dart @@ -0,0 +1,154 @@ +import 'package:dio/dio.dart'; + +class AppRequest { + + static const Duration defaultTimeout = Duration(seconds: 15); + static final AppRequest instance = AppRequest(); + final Dio dio; + + AppRequest({ + Dio? dio, + String baseUrl = '', + Duration connectTimeout = defaultTimeout, + Duration sendTimeout = defaultTimeout, + Duration receiveTimeout = defaultTimeout, + }) : dio = + dio ?? + Dio( + BaseOptions( + baseUrl: baseUrl, + connectTimeout: connectTimeout, + sendTimeout: sendTimeout, + receiveTimeout: receiveTimeout, + responseType: ResponseType.json, + ), + ); + + + + void setBaseUrl(String baseUrl) { + dio.options.baseUrl = baseUrl; + } + + void setHeader(String key, Object? value) { + dio.options.headers[key] = value; + } + + void removeHeader(String key) { + dio.options.headers.remove(key); + } + + void setToken(String token, {String prefix = 'Bearer'}) { + final value = prefix.isEmpty ? token : '$prefix $token'; + setHeader('Authorization', value); + } + + void clearToken() { + removeHeader('Authorization'); + } + + Future get( + String path, { + Map? queryParameters, + Options? options, + CancelToken? cancelToken, + ProgressCallback? onReceiveProgress, + }) { + return request( + path, + method: 'GET', + queryParameters: queryParameters, + options: options, + cancelToken: cancelToken, + onReceiveProgress: onReceiveProgress, + ); + } + + Future post( + String path, { + Object? data, + Map? queryParameters, + Options? options, + CancelToken? cancelToken, + ProgressCallback? onSendProgress, + ProgressCallback? onReceiveProgress, + }) { + return request( + path, + method: 'POST', + data: data, + queryParameters: queryParameters, + options: options, + cancelToken: cancelToken, + onSendProgress: onSendProgress, + onReceiveProgress: onReceiveProgress, + ); + } + + Future put( + String path, { + Object? data, + Map? queryParameters, + Options? options, + CancelToken? cancelToken, + ProgressCallback? onSendProgress, + ProgressCallback? onReceiveProgress, + }) { + return request( + path, + method: 'PUT', + data: data, + queryParameters: queryParameters, + options: options, + cancelToken: cancelToken, + onSendProgress: onSendProgress, + onReceiveProgress: onReceiveProgress, + ); + } + + Future delete( + String path, { + Object? data, + Map? queryParameters, + Options? options, + CancelToken? cancelToken, + }) { + return request( + path, + method: 'DELETE', + data: data, + queryParameters: queryParameters, + options: options, + cancelToken: cancelToken, + ); + } + + Future request( + String path, { + required String method, + Object? data, + Map? queryParameters, + Options? options, + CancelToken? cancelToken, + ProgressCallback? onSendProgress, + ProgressCallback? onReceiveProgress, + }) async { + final response = await dio.request( + path, + data: data, + queryParameters: queryParameters, + options: _withMethod(options, method), + cancelToken: cancelToken, + onSendProgress: onSendProgress, + onReceiveProgress: onReceiveProgress, + ); + + return response.data; + } + + Options _withMethod(Options? options, String method) { + return options == null + ? Options(method: method) + : options.copyWith(method: method); + } +} diff --git a/lib/main.dart b/lib/main.dart index 244a702..ec883e3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,122 +1,65 @@ import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:skywood/main/main_root.dart'; -void main() { - runApp(const MyApp()); +import 'global/const/common_color.dart'; +import 'global/language/app_language.dart'; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + // await initServices(); + + Locale? locale = await AppLanguage.getLocale(); + runApp(ComApp( + home: MainRoot(), + locale: locale, + )); } -class MyApp extends StatelessWidget { - const MyApp({super.key}); - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // TRY THIS: Try running your application with "flutter run". You'll see - // the application has a purple toolbar. Then, without quitting the app, - // try changing the seedColor in the colorScheme below to Colors.green - // and then invoke "hot reload" (save your changes or press the "hot - // reload" button in a Flutter-supported IDE, or press "r" if you used - // the command line to start the app). - // - // Notice that the counter didn't reset back to zero; the application - // state is not lost during the reload. To reset the state, use hot - // restart instead. - // - // This works for code too, not just values: Most code changes can be - // tested with just a hot reload. - colorScheme: .fromSeed(seedColor: Colors.deepPurple), - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); +class ComApp extends StatelessWidget { + const ComApp({super.key, + required this.home, + this.locale, + }); - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } + final Widget home; + final Locale? locale; @override Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - // - // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" - // action in the IDE, or press "p" in the console), to see the - // wireframe for each widget. - mainAxisAlignment: .center, - children: [ - const Text('You have pushed the button this many times:'), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), - ], + // Locale locale = await AppLanguage.getLocale(); + return ScreenUtilInit( + designSize: Size(375, 812), + child: GetMaterialApp( + debugShowCheckedModeBanner: false, + translations: AppLanguage(), + locale: locale ?? Get.deviceLocale, + fallbackLocale: const Locale('en'), + // initialBinding: AppBindings(), + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: CommonColor.primacyColor), + appBarTheme: AppBarTheme(elevation: 0, centerTitle: true), + splashColor: Colors.transparent, + useMaterial3: true, + scaffoldBackgroundColor: Colors.white, ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), + home: home, + builder: FlutterSmartDialog.init(builder: (context, child) { + return MediaQuery( + data: MediaQuery.of(context).copyWith(textScaler: TextScaler.linear(1.0)), + child: child! + ); + }), + onInit: () { + + }, ), ); } + } diff --git a/lib/main/lib_file.dart b/lib/main/lib_file.dart new file mode 100644 index 0000000..7255fa0 --- /dev/null +++ b/lib/main/lib_file.dart @@ -0,0 +1,8 @@ + +export 'package:skywood/utils/extend/widget_extend.dart'; + +export 'package:skywood/global/const/common_color.dart'; +export 'package:skywood/global/const/common_string.dart'; + +export 'package:skywood/utils/tools/app_screen.dart'; +export 'package:skywood/utils/tools/help_img.dart'; \ No newline at end of file diff --git a/lib/main/main_root.dart b/lib/main/main_root.dart new file mode 100644 index 0000000..eb07827 --- /dev/null +++ b/lib/main/main_root.dart @@ -0,0 +1,140 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:skywood/pages/home/home_page.dart'; +import 'package:skywood/pages/like/like_page.dart'; +import 'package:skywood/pages/mine/mine_page.dart'; +import 'package:skywood/pages/search/search_page.dart'; +import 'package:skywood/utils/tools/help_img.dart'; + +class MainRoot extends StatefulWidget { + const MainRoot({super.key}); + + @override + State createState() { + return _MainRootState(); + } +} + +class _MainRootState extends State { + final List _pages = const [ + HomePage(), + SearchPage(), + LikePage(), + MinePage(), + ]; + + final List<_TabBarItem> _items = const [ + _TabBarItem( + selectedIcon: ConstImg.tabHomeSelected, + unselectedIcon: ConstImg.tabHomeUnselect, + key: Key('main-tab-home'), + ), + _TabBarItem( + selectedIcon: ConstImg.tabSearchSelected, + unselectedIcon: ConstImg.tabSearchUnselect, + key: Key('main-tab-search'), + ), + _TabBarItem( + selectedIcon: ConstImg.tabLikeSelected, + unselectedIcon: ConstImg.tabLikeUnselect, + key: Key('main-tab-like'), + ), + _TabBarItem( + selectedIcon: ConstImg.tabMineSelected, + unselectedIcon: ConstImg.tabMineUnselect, + key: Key('main-tab-mine'), + ), + ]; + + int _currentIndex = 0; + + @override + Widget build(BuildContext context) { + return Scaffold( + extendBody: true, + body: IndexedStack(index: _currentIndex, children: _pages), + bottomNavigationBar: SafeArea( + top: false, + minimum: EdgeInsets.only(bottom: 10.h), + child: Stack( + children: [ + SizedBox( + height: 49.h, width: 345.w, + ), + Positioned( + top: 11.h, + child: _buildTabBar(), + ), + Positioned( + top: 0, left: 15.w, + child: _buildImgs(), + ) + ], + ), + ), + ); + } + + Widget _buildImgs() { + return Container( + height: 38.h, width: 345.w, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: List.generate(_items.length, (index) { + final item = _items[index]; + final isSelected = index == _currentIndex; + + return GestureDetector( + key: item.key, + behavior: HitTestBehavior.opaque, + onTap: () => _changeTab(index), + child: Center( + child: Image.asset( + isSelected ? item.selectedIcon : item.unselectedIcon, + width: 40.w, + height: 40.w, + ), + ), + ); + }), + ), + ); + } + + Widget _buildTabBar() { + return Container( + key: const Key('main-tab-bar'), + width: 345.w, + height: 38.h, + margin: EdgeInsets.symmetric(horizontal: 15.w), + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage(ConstImg.bottomBar), + fit: BoxFit.fill, + ), + ), + ); + } + + void _changeTab(int index) { + if (_currentIndex == index) { + return; + } + + setState(() { + _currentIndex = index; + }); + } +} + +class _TabBarItem { + const _TabBarItem({ + required this.selectedIcon, + required this.unselectedIcon, + required this.key, + }); + + final String selectedIcon; + final String unselectedIcon; + final Key key; +} diff --git a/lib/pages/home/home_page.dart b/lib/pages/home/home_page.dart new file mode 100644 index 0000000..539a0ca --- /dev/null +++ b/lib/pages/home/home_page.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:skywood/pages/widgets/common_bg_page.dart'; + +class HomePage extends StatefulWidget { + + const HomePage({super.key}); + + @override + State createState() { + return _MainRootState(); + } +} + +class _MainRootState extends State { + + @override + Widget build(BuildContext context) { + return CommonBgPage( + child: CustomScrollView( + slivers: [ + + ], + ), + ); + } + + Widget _buildTitleImg() { + return Container(); + } +} \ No newline at end of file diff --git a/lib/pages/like/like_page.dart b/lib/pages/like/like_page.dart new file mode 100644 index 0000000..e193650 --- /dev/null +++ b/lib/pages/like/like_page.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class LikePage extends StatefulWidget { + + const LikePage({super.key}); + + @override + State createState() { + return _MainRootState(); + } +} + +class _MainRootState extends State { + + @override + Widget build(BuildContext context) { + return Scaffold( + + ); + } +} \ No newline at end of file diff --git a/lib/pages/mine/mine_page.dart b/lib/pages/mine/mine_page.dart new file mode 100644 index 0000000..b6d8936 --- /dev/null +++ b/lib/pages/mine/mine_page.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class MinePage extends StatefulWidget { + + const MinePage({super.key}); + + @override + State createState() { + return _MainRootState(); + } +} + +class _MainRootState extends State { + + @override + Widget build(BuildContext context) { + return Scaffold( + + ); + } +} \ No newline at end of file diff --git a/lib/pages/search/search_page.dart b/lib/pages/search/search_page.dart new file mode 100644 index 0000000..5e7d835 --- /dev/null +++ b/lib/pages/search/search_page.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class SearchPage extends StatefulWidget { + + const SearchPage({super.key}); + + @override + State createState() { + return _MainRootState(); + } +} + +class _MainRootState extends State { + + @override + Widget build(BuildContext context) { + return Scaffold( + + ); + } +} \ No newline at end of file diff --git a/lib/pages/widgets/common_bg_page.dart b/lib/pages/widgets/common_bg_page.dart new file mode 100644 index 0000000..8b25fee --- /dev/null +++ b/lib/pages/widgets/common_bg_page.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:skywood/utils/tools/help_img.dart'; + +class CommonBgPage extends StatelessWidget { + + const CommonBgPage({ + super.key, + this.child, + + }); + + final Widget? child; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + children: [ + SizedBox( + width: double.infinity, + height: double.infinity, + child: HelpImg.asset(ConstImg.homeBg, fit: BoxFit.fill), + ), + child ?? SizedBox() + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/pages/widgets/player_widget.dart b/lib/pages/widgets/player_widget.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/utils/extend/app_keep_alive.dart b/lib/utils/extend/app_keep_alive.dart new file mode 100644 index 0000000..e02d178 --- /dev/null +++ b/lib/utils/extend/app_keep_alive.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +/// keep alive +class AppKeepAlive extends StatefulWidget { + final Widget child; + final bool? wantKeepAlive; + + const AppKeepAlive( + {super.key, required this.child, this.wantKeepAlive = true}); + + @override + State createState() => _CmKeepAlivePageState(); +} + +class _CmKeepAlivePageState extends State + with AutomaticKeepAliveClientMixin { + @override + Widget build(BuildContext context) { + super.build(context); + return widget.child; + } + + @override + bool get wantKeepAlive => widget.wantKeepAlive!; +} diff --git a/lib/utils/extend/widget_extend.dart b/lib/utils/extend/widget_extend.dart new file mode 100644 index 0000000..7c4008d --- /dev/null +++ b/lib/utils/extend/widget_extend.dart @@ -0,0 +1,205 @@ +import 'package:flutter/material.dart'; + + +// 扩展Widget类以添加额外的布局和样式方法 +extension WidgetExtend on Widget { + /// 在当前Widget外层添加一个Container + /// + /// 可以通过此方法设置容器的颜色、大小、边距、内边距、对齐方式等 + /// 如果设置了gradientColors,则会应用渐变色 + /// 边框和圆角也可以通过此方法自定义 + /// 添加点击事件处理函数onTap会创建一个InkWell + Widget addContainer({ + Color? color, + double? height, + double? width, + EdgeInsets? margin, + EdgeInsets? padding, + double? radius, + BorderRadius? borderRadius, + Border? border, + GestureTapCallback? onTap, + AlignmentGeometry? alignment, + Gradient? gradient, + List? boxShadow, + BoxConstraints? constraints, + Matrix4? transform, + Clip clipBehavior = Clip.none, + DecorationImage? image, + BlendMode? backgroundBlendMode, + BoxShape shape = BoxShape.rectangle, + double? minWidth, + double? maxWidth, + double? minHeight, + double? maxHeight, + + /// 打印 widget size 只在 debug 有效 + bool isPrintSize = false, + }) { + return Container( + width: width, + height: height, + margin: margin, + padding: padding, + alignment: alignment, + clipBehavior: clipBehavior, + constraints: constraints ?? + BoxConstraints( + minWidth: minWidth ?? 0.0, + maxWidth: maxWidth ?? double.infinity, + minHeight: minHeight ?? 0.0, + maxHeight: maxHeight ?? double.infinity, + ), + transform: transform, + decoration: BoxDecoration( + color: color, + boxShadow: boxShadow, + backgroundBlendMode: backgroundBlendMode, + shape: shape, + image: image, + gradient: gradient, + border: border, + borderRadius: + radius != null ? BorderRadius.circular(radius) : borderRadius, + ), + child: this, + ).addInkWell(onTap: onTap); + } + + /// 在当前Widget外层添加一个Transform.translate + /// + /// 通过此方法可以设置控件的偏移量,实现位置移动的效果 + Widget addOffset({Offset offset = const Offset(0, 0)}) { + return Transform.translate(offset: offset, child: this); + } + + /// 在当前Widget外层添加一个Padding + /// + /// 通过此方法可以设置控件的内边距 + Widget addPadding({EdgeInsets? padding}) { + return Padding(padding: padding ?? EdgeInsets.zero, child: this); + } + + /// 在当前Widget外层添加一个Align + /// + /// 通过此方法可以设置控件的对齐方式 + Widget addAlign({AlignmentGeometry alignment = Alignment.centerLeft}) { + return Align( + alignment: alignment, + child: this, + ); + } + + Widget addCenter() { + return Center(child: this); + } + + /// 在当前Widget外层添加一个GestureDetector + /// + /// 通过此方法可以为控件添加点击事件处理函数 + Widget addGestureDetector({ + GestureTapCallback? onTap, + GestureTapCallback? onDoubleTap, + GestureLongPressCallback? onLongPress, + }) { + return GestureDetector( + onTap: onTap, + onDoubleTap: onDoubleTap, + onLongPress: onLongPress, + child: this, + ); + // return GestureDetectorDebounced( + // onTap: onTap, + // child: this, + // ); + } + + /// 在当前Widget外层添加一个Visibility + /// + /// 通过此方法可以控制控件的显示和隐藏 + Widget addVisibility({bool visible = true}) { + return Visibility( + visible: visible, + child: this, + ); + } + + /// 在当前Widget外层添加一个Transform.rotate + /// + /// 通过此方法可以设置控件的旋转角度 + Widget addRotate({double angle = 0}) { + return Transform.rotate( + angle: angle, + child: this, + ); + } + + /// 在当前Widget外层添加一个InkWell + /// + /// 通过此方法可以为控件添加点击事件处理函数 + Widget addInkWell({GestureTapCallback? onTap}) { + if (onTap == null) { + return this; + } + return InkWell( + onTap: onTap, + child: this, + ); + } + + + + /// 在当前Widget外层添加一个SizedBox + /// + /// 通过此方法可以设置控件的宽高 + Widget addSize({double? width, double? height}) { + return SizedBox( + width: width, + height: height, + child: this, + ); + } + + /// 在当前Widget外层添加一个Opacity + /// + /// 通过此方法可以设置控件的透明度 + Widget addOpacity({double opacity = 1}) { + return Opacity( + opacity: opacity, + child: this, + ); + } +} + + + +extension OnRenderObjectWidget on RenderObjectWidget { + SliverPadding addPaddingSliver({EdgeInsets? padding}) { + return SliverPadding( + padding: padding ?? EdgeInsets.zero, + sliver: this, + ); + } + + DecoratedSliver addDecoratedSliver({ + Color? color, + BorderRadius? borderRadius, + Border? border, + BoxShape? shape, + Gradient? gradient, + List? boxShadow, + EdgeInsets? padding, + }) { + return DecoratedSliver( + decoration: BoxDecoration( + color: color ?? Colors.white, + border: border, + borderRadius: borderRadius, + boxShadow: boxShadow, + gradient: gradient, + shape: shape ?? BoxShape.rectangle, + ), + sliver: this.addPaddingSliver(padding: padding), + ); + } +} diff --git a/lib/utils/tools/app_screen.dart b/lib/utils/tools/app_screen.dart new file mode 100644 index 0000000..dd9e5e6 --- /dev/null +++ b/lib/utils/tools/app_screen.dart @@ -0,0 +1,44 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +/// AppScreen class +class AppScreen { + static double get screenWidth => Get.width; + static double get screenHeight => Get.height; + // static double get screenWidth => MediaQuery.of(Get.context!).size.width; + // static double get screenHeight => MediaQuery.of(Get.context!).size.height; + + static Size size(BuildContext context) { + return MediaQuery.of(context).size; + } + + static bool get isDebug=> kDebugMode; + + static double get statusBarHeight => Get.statusBarHeight / Get.pixelRatio; + + static double get bottomBarHeight => Get.bottomBarHeight / Get.pixelRatio; + + // static double get tabBarHeight => kToolbarHeight; + + static double get navBarHeight => kToolbarHeight + statusBarHeight; + + static bool get isDark => Get.isDarkMode; + + static bool get isAndroid { + return GetPlatform.isAndroid; + } + + static bool get isIos { + return GetPlatform.isIOS; + } + + static double aRatioWidth({double? h, double aspectRatio = 1}) { + h ??= screenHeight; + return screenHeight * 1 / aspectRatio; + } + + + static double get keyboardHeight => Get.mediaQuery.viewInsets.bottom; + +} diff --git a/lib/utils/tools/app_sp.dart b/lib/utils/tools/app_sp.dart new file mode 100644 index 0000000..ae3e9e5 --- /dev/null +++ b/lib/utils/tools/app_sp.dart @@ -0,0 +1,59 @@ + +import 'package:shared_preferences/shared_preferences.dart'; + + +// 用法: AppSp().setString("key", "value"); +class AppSp { + static SharedPreferences? _instance; + static final String _basePrefix = "omiyo."; + static Future? _instanceFuture; + + static Future getInstance() { + if (_instance != null) { + return Future.value(_instance); + } + + // 确保并发情况下只初始化一次 + _instanceFuture ??= SharedPreferences.getInstance().then((prefs) { + _instance = prefs; + return prefs; + }); + + return _instanceFuture!; + } + + void setString(String key, String value) async { + final prefs = await getInstance(); + prefs.setString("$_basePrefix$key", value); + } + + Future getString(String key) async { + final prefs = await getInstance(); + return prefs.getString("$_basePrefix$key") ?? ''; + } + + void setBool(String key, bool value) async { + final prefs = await getInstance(); + prefs.setBool("$_basePrefix$key", value); + } + + Future getBool(String key) async { + final prefs = await getInstance(); + return prefs.getBool("$_basePrefix$key") ?? false; + } + + void setInt(String key, int value) async { + final prefs = await getInstance(); + prefs.setInt("$_basePrefix$key", value); + } + + Future getInt(String key) async { + final prefs = await getInstance(); + return prefs.getInt("$_basePrefix$key") ?? 0; + } + + void deleteKey(String key) async { + final prefs = await getInstance(); + prefs.remove("$_basePrefix$key"); + } +} diff --git a/lib/utils/tools/help_img.dart b/lib/utils/tools/help_img.dart new file mode 100644 index 0000000..4f0846c --- /dev/null +++ b/lib/utils/tools/help_img.dart @@ -0,0 +1,137 @@ +import 'package:flutter/material.dart'; +// import 'package:lottie/lottie.dart'; +// import 'package:flutter_svg/flutter_svg.dart'; + +class ConstImg { + ConstImg._(); + + static const String bottomBar = "assets/common/bottom_bar.png"; + static const String tabHomeSelected = "assets/common/tab_home_selected.png"; + static const String tabHomeUnselect = "assets/common/tab_home_unselect.png"; + static const String tabSearchSelected = + "assets/common/tab_search_selected.png"; + static const String tabSearchUnselect = + "assets/common/tab_search_unselect.png"; + static const String tabLikeSelected = "assets/common/tab_like_selected.png"; + static const String tabLikeUnselect = "assets/common/tab_like_unselect.png"; + static const String tabMineSelected = "assets/common/tab_mine_selected.png"; + static const String tabMineUnselect = "assets/common/tab_mine_unselect.png"; + static const String homeBg = "assets/imgs/home_bg.png"; + + // nodata + static const String noData = "assets/images/nodata.png"; + + static const String loadingJson = "assets/bagua.json"; + // static get appLoading => Lottie.asset(loadingJson, width: 60, height: 60); +} + +class HelpImg { + /// load local + static Widget asset( + String path, { + BoxFit? fit, + double? width, + double? height, + Color? color, + bool isDartData = false, + String? dartPath, + }) { + String assets = path; + + return Image.asset( + assets, + fit: fit, + width: width, + height: height, + color: color, + ); + } + + static Widget networkRadius( + String path, { + BoxFit? fit, + double? width, + double? height, + BoxShape? shape, + BorderRadius? borderRadius, + Widget? errorWidget, + }) { + return ClipRRect( + borderRadius: borderRadius ?? BorderRadius.circular(3), + child: network( + path, + fit: fit, + width: width, + height: height, + shape: shape, + errorWidget: errorWidget, + ), + ); + } + + /// load network + static Widget network( + String path, { + BoxFit? fit, + double? width, + double? height, + Color? color, + BoxShape? shape, + BorderRadius? borderRadius, + Widget? errorWidget, + }) { + // bool isSvg = path.toLowerCase().endsWith('.svg'); + // if (isSvg) { + // return SvgPicture.network( + // path, + // width: width, + // height: height, + // // fit: fit ?? BoxFit.fill, + // colorFilter: color != null + // ? ColorFilter.mode(color, BlendMode.srcIn) + // : null, + // placeholderBuilder: (context) => Container( + // width: width, + // height: height, + // color: Colors.grey[200], + // child: Center(child: CircularProgressIndicator()), + // ), + // ); + // } + return Image.network( + path, + width: width, + height: height, + fit: fit, + loadingBuilder: (context, child, loadingProgress) { + if (loadingProgress == null) return child; + return Center( + //CircularProgressIndicator + child: Container( + width: 50, + height: 50, + padding: const EdgeInsets.all(5), + child: CircularProgressIndicator( + color: Colors.white, + strokeWidth: 2, + ), + ), + ); + }, + errorBuilder: (context, error, stackTrace) { + // ✅ 关键:处理加载失败的情况 + return errorWidget ?? + Container( + width: 50, + height: 50, + color: Colors.grey[300], + child: const Icon( + Icons.broken_image, + color: Colors.grey, + size: 40, + ), + ); + }, + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 66337f2..aeabfe8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -169,6 +169,22 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.3" + dio: + dependency: "direct main" + description: + name: dio + sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c + url: "https://pub.dev" + source: hosted + version: "5.9.2" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340" + url: "https://pub.dev" + source: hosted + version: "2.1.2" easy_refresh: dependency: "direct main" description: @@ -446,6 +462,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" + flutter_smart_dialog: + dependency: "direct main" + description: + name: flutter_smart_dialog + sha256: "18ee3a53e898e4b0c2898e498bb61ab65cedf56f00fc412ac66d986d33e246bf" + url: "https://pub.dev" + source: hosted + version: "5.1.0" flutter_staggered_grid_view: dependency: "direct main" description: @@ -880,6 +904,62 @@ packages: url: "https://pub.dev" source: hosted version: "0.28.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf + url: "https://pub.dev" + source: hosted + version: "2.5.5" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: e8d4762b1e2e8578fc4d0fd548cebf24afd24f49719c08974df92834565e2c53 + url: "https://pub.dev" + source: hosted + version: "2.4.23" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" + url: "https://pub.dev" + source: hosted + version: "2.5.6" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" sign_in_with_apple: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index c4861bd..4c3d390 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: skywood description: "A new Flutter project." -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: 'none' # Remove this like if you wish to publish to pub.dev version: 1.0.0+1 @@ -46,6 +46,10 @@ dependencies: logger: ^2.5.0 loading_animation_widget: ^1.3.0 + dio: ^5.9.2 + shared_preferences: ^2.5.3 + flutter_smart_dialog: ^5.1.0 + dev_dependencies: flutter_test: sdk: flutter @@ -61,8 +65,9 @@ flutter: uses-material-design: true # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg + assets: + - assets/common/ + - assets/imgs/ # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see diff --git a/test/widget_test.dart b/test/widget_test.dart index 7942f2b..eaeb005 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -9,11 +9,12 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:skywood/main.dart'; +import 'package:skywood/main/main_root.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); + await tester.pumpWidget(const ComApp(home: MainRoot())); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget);