├── .gitignore ├── .metadata ├── README.md ├── README_CN.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── flutter_quick_template │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h └── RunnerTests │ └── RunnerTests.swift ├── lib ├── Initializer.dart ├── base │ ├── LightStorage │ │ ├── light_storage.dart │ │ ├── mmkv_light_storage.dart │ │ └── shared_preference_light_storage.dart │ ├── base_controller.dart │ ├── base_page.dart │ ├── base_scaffold_page.dart │ ├── network │ │ ├── api_client.dart │ │ ├── api_client.g.dart │ │ ├── api_exception.dart │ │ ├── api_result_interceptor.dart │ │ ├── error_interceptor.dart │ │ ├── http.dart │ │ ├── mock_interceptor.dart │ │ ├── result.dart │ │ ├── result.g.dart │ │ ├── test_info.dart │ │ └── test_info.g.dart │ ├── page_life_state.dart │ └── widgets │ │ └── net_image.dart ├── events │ └── text_event.dart ├── generated │ ├── locales.g.dart │ └── route_table.dart ├── main.dart ├── page │ ├── event_test_page.dart │ ├── home_page.dart │ ├── http_page.dart │ └── light_storage_page.dart └── utils │ ├── event_bus.dart │ ├── global_extension.dart │ ├── loading_util.dart │ ├── log_util.dart │ └── toast_util.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake ├── main.cc ├── my_application.cc └── my_application.h ├── macos ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── app_icon_1024.png │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_512.png │ │ │ └── app_icon_64.png │ ├── Base.lproj │ │ └── MainMenu.xib │ ├── Configs │ │ ├── AppInfo.xcconfig │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── Warnings.xcconfig │ ├── DebugProfile.entitlements │ ├── Info.plist │ ├── MainFlutterWindow.swift │ └── Release.entitlements └── RunnerTests │ └── RunnerTests.swift ├── pubspec.yaml ├── test └── widget_test.dart ├── translations ├── en_US.json └── zh_CN.json ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── index.html └── manifest.json └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter ├── CMakeLists.txt ├── generated_plugin_registrant.cc ├── generated_plugin_registrant.h └── generated_plugins.cmake └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .packages 31 | .pub-cache/ 32 | .pub/ 33 | /build/ 34 | 35 | # Symbolication related 36 | app.*.symbols 37 | 38 | # Obfuscation related 39 | app.*.map.json 40 | 41 | # Android Studio will place build artifacts here 42 | /android/app/debug 43 | /android/app/profile 44 | /android/app/release 45 | /.history 46 | /.vscode 47 | /.idea 48 | /.dart_tool 49 | .history 50 | pubspec.lock 51 | 52 | # FVM Version Cache 53 | .fvm/ -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "e1e47221e86272429674bec4f1bd36acc4fc7b77" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 17 | base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 18 | - platform: android 19 | create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 20 | base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 21 | - platform: ios 22 | create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 23 | base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 24 | - platform: linux 25 | create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 26 | base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 27 | - platform: macos 28 | create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 29 | base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 30 | - platform: web 31 | create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 32 | base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 33 | - platform: windows 34 | create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 35 | base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flutter_quick_template 2 | Language: [中文](README_CN.md) | English 3 | 4 | This is a Flutter basic development framework built on third-party open-source libraries, providing convenient and easy-to-use utility classes and methods for quickly setting up a new Flutter project. The current project utilizes the following open-source libraries, and we express our gratitude for their contributions: 5 | 6 | - [getx_route_generator ](https://github.com/panyiho/getx_route_generator) 7 | - [getx](https://github.com/jonataslaw/getx) 8 | - [flutter_screenutil](https://github.com/OpenFlutter/flutter_screenutil) 9 | - [lifecycle](https://github.com/chenenyu/lifecycle) 10 | - [shared_preferences](https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences) 11 | - [dio](https://github.com/cfug/dio/tree/main/dio) 12 | - [logger](https://github.com/SourceHorizon/logger) 13 | - [retrofit](https://github.com/trevorwang/retrofit.dart/) 14 | - [json_annotation](https://github.com/google/json_serializable.dart/tree/master/json_annotation) 15 | - [cached_network_image](https://github.com/Baseflow/flutter_cached_network_image) 16 | - [flutter_smart_dialog](https://github.com/fluttercandies/flutter_smart_dialog) 17 | - [mmkv](https://github.com/Tencent/mmkv) 18 | - [styled_widget](https://github.com/ReinBentdal/styled_widget) 19 | - [pretty_dio_logger](https://github.com/Milad-Akarie/pretty_dio_logger) 20 | 21 | 22 | ## Usage 23 | 24 | ### BasePage and BaseController 25 | `BasePage` inherits from `GetView` of GetX, corresponding to a separate route page. It also utilizes the `Lifecycle` library to provide lifecycle callbacks for page display, including: 26 | 27 | - `onPageActive` 28 | - `onPageVisible` 29 | - `onPageInActive` 30 | - `onPageInVisible` 31 | 32 | These correspond to page activation, visibility, inactivity, and invisibility, similar to the `onResume` callback in Android's `Activity`. Here, since we are not using GetX's bindings feature, you need to override the `generateController` method to provide the `BaseController` bound to the current page. 33 | 34 | ``` dart 35 | abstract class BasePage extends GetView { 36 | BuildContext? context; 37 | 38 | T generateController(); 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | this.context = context; 43 | Get.put(generateController()); 44 | return LifecycleWrapper( 45 | onLifecycleEvent: (LifecycleEvent lifecycleEvent) { 46 | logI("$runtimeType $lifecycleEvent"); 47 | switch (lifecycleEvent) { 48 | case LifecycleEvent.visible: 49 | controller.onPageVisible(); 50 | break; 51 | case LifecycleEvent.inactive: 52 | controller.onPageInActive(); 53 | break; 54 | case LifecycleEvent.active: 55 | controller.onPageActive(); 56 | break; 57 | case LifecycleEvent.invisible: 58 | controller.onPageInVisible(); 59 | break; 60 | default: 61 | } 62 | }, 63 | child: GetBuilder( 64 | builder: buildWidget, 65 | assignId: true, 66 | )); 67 | } 68 | 69 | Widget buildWidget(T controller); 70 | } 71 | 72 | ``` 73 | ### BaseController 74 | BaseController inherits from SuperController of GetX. Therefore, BaseController has the app lifecycle callbacks just like SuperController. Additionally, it has the page lifecycle callbacks mentioned above and provides eventBus event listening, enabling event communication between different BaseController instances. Simply override the getListenEvent method to return the types of events you want to listen for, and the onReceiveEvent method will be called when an event of that type is sent. 75 | 76 | ``` dart 77 | class HomePageController extends BaseController { 78 | @override 79 | List getListenEvent() { 80 | return [CustomTextEvent]; 81 | } 82 | 83 | @override 84 | void onReceiveEvent(event) { 85 | switch (event.runtimeType) { 86 | case CustomTextEvent: 87 | Toast.show(event.text); 88 | break; 89 | default: 90 | } 91 | } 92 | } 93 | ``` 94 | BaseController automatically registers and unregisters eventBus event listeners in onReady and onClose, respectively, eliminating the need for manual handling. 95 | 96 | ### Generating Page Routing Table 97 | To simplify the use of get for routing purposes and improve efficiency, I've created an open-source library called `getx_route_generator`. By adding the GetXRoutePage annotation to the BasePage and running a simple command, a handy routing table is automatically created. Here's an example of the generated code: 98 | ``` dart 99 | // ignore_for_file: constant_identifier_names 100 | import 'package:get/get.dart'; 101 | import 'package:flutter_quick_template/page/home_page.dart'; 102 | 103 | class RouteTable { 104 | static const String home = '/home'; 105 | 106 | static final List pages = [ 107 | GetPage(name: '/home', page: () => HomePage()), 108 | ]; 109 | } 110 | ``` 111 | For more details, check out :[getx_route_generator](https://pub.dev/packages/getx_route_generator) 112 | 113 | ### Lightweight Storage 114 | In the development of an app, there is often a need to persistently store key-value data or configurations. On Android, SharedPreferences is commonly used, and on iOS, it corresponds to NSUserDefaults. This project provides encapsulated classes based on SharedPreferences and MMKV for out-of-the-box use. Choose according to your needs: 115 | 116 | ``` dart 117 | LightStorage.getString(key, defaultValue); 118 | LightStorage.clear(); 119 | LightStorage.putString(key, defaultValue); 120 | LightStorage.containsKey(key); 121 | LightStorage.remove(key); 122 | ``` 123 | ### Utils Utility Classes 124 | This project provides three utility classes for commonly used features: loading, toast, and log. They are encapsulated to isolate dependencies, allowing for easy switching without modifying a lot of code if customization or different underlying implementations are required. 125 | 126 | #### Loading 127 | ``` dart 128 | Loading.show(); 129 | Loading.dismiss(); 130 | ``` 131 | #### Toast 132 | ``` dart 133 | Toast.show(); 134 | ``` 135 | #### Log 136 | ``` dart 137 | logV("log msg"); 138 | logD("log msg"); 139 | logI("log msg"); 140 | logW("log msg"); 141 | logE("log msg"); 142 | logWTF("log msg"); 143 | ``` 144 | #### Extensions 145 | EventBus 146 | You can directly send events: 147 | 148 | ``` dart 149 | CustomTextEvent(text: "msg from EventTestPage").sendEvent(); 150 | ``` 151 | #### GestureDetector 152 | ``` dart 153 | Text("onTab").onTab((){}); 154 | Text("onDoubleTab").onDoubleTab((){}); 155 | Text("onLongPress").onLongPress((){}); 156 | ``` 157 | #### Widgets 158 | All use the extensions method from the styled_widget library, like this: 159 | 160 | ``` dart 161 | Icon(OMIcons.home, color: Colors.white) 162 | .padding(all: 10) 163 | .decorated(color: Color(0xff7AC1E7), shape: BoxShape.circle) 164 | .padding(all: 15) 165 | .decorated(color: Color(0xffE8F2F7), shape: BoxShape.circle) 166 | .padding(all: 20) 167 | .card( 168 | elevation: 10, 169 | shape: RoundedRectangleBorder( 170 | borderRadius: BorderRadius.circular(20), 171 | ), 172 | ) 173 | .alignment(Alignment.center) 174 | .backgroundColor(Color(0xffEBECF1)); 175 | ``` 176 | For more usage, refer to the styled_widget library's official documentation: https://github.com/ReinBentdal/styled_widget 177 | 178 | #### Http 179 | HTTP requests are implemented using the dio and retrofit libraries, as shown below: 180 | 181 | ``` dart 182 | Result result = await Http.client().getInfo(); 183 | Toast.show("${result.data}"); 184 | ``` 185 | For more details, please refer to the source code. 186 | 187 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # flutter_quick_template 2 | Language: 中文 | [English](README.md) 3 | 4 | 这是一款基于第三方开源库搭建的flutter基础开发架构框架,提供了很多方便易用的工具类和方法,用于快速搭建和开启一个新的flutter项目,当前项目用到了以下几个开源库,非常感谢他们的开源贡献。 5 | 6 | - [getx_route_generator ](https://github.com/panyiho/getx_route_generator) 7 | - [getx](https://github.com/jonataslaw/getx) 8 | - [flutter_screenutil](https://github.com/OpenFlutter/flutter_screenutil) 9 | - [lifecycle](https://github.com/chenenyu/lifecycle) 10 | - [shared_preferences](https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences) 11 | - [dio](https://github.com/cfug/dio/tree/main/dio) 12 | - [logger](https://github.com/SourceHorizon/logger) 13 | - [retrofit](https://github.com/trevorwang/retrofit.dart/) 14 | - [json_annotation](https://github.com/google/json_serializable.dart/tree/master/json_annotation) 15 | - [cached_network_image](https://github.com/Baseflow/flutter_cached_network_image) 16 | - [flutter_smart_dialog](https://github.com/fluttercandies/flutter_smart_dialog) 17 | - [mmkv](https://github.com/Tencent/mmkv) 18 | - [styled_widget](https://github.com/ReinBentdal/styled_widget) 19 | - [pretty_dio_logger](https://github.com/Milad-Akarie/pretty_dio_logger) 20 | 21 | 22 | ## 使用方式 23 | ### BasePage和BaseController 24 | BasePage继承于get的getView,对应着一个单独的路由页面,同时利用LifeCycle库提供页面显示的生命周期回调,分别为: 25 | onPageActive, 26 | onPageVisible, 27 | onPageInActive, 28 | onPageInVisible 29 | ,对应着页面激活,页面可见,页面不可见等状态,类似于Android Activity的onResume回调,这里因为没有用到getx的bindings功能,所以需要自己复写generateController方法提供当前页面绑定的BaseController。 30 | ``` dart 31 | abstract class BasePage extends GetView { 32 | BuildContext? context; 33 | 34 | T generateController(); 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | this.context = context; 39 | Get.put(generateController()); 40 | return LifecycleWrapper( 41 | onLifecycleEvent: (LifecycleEvent lifecycleEvent) { 42 | logI("$runtimeType $lifecycleEvent"); 43 | switch (lifecycleEvent) { 44 | case LifecycleEvent.visible: 45 | controller.onPageVisible(); 46 | break; 47 | case LifecycleEvent.inactive: 48 | controller.onPageInActive(); 49 | break; 50 | case LifecycleEvent.active: 51 | controller.onPageActive(); 52 | break; 53 | case LifecycleEvent.invisible: 54 | controller.onPageInVisible(); 55 | break; 56 | default: 57 | } 58 | }, 59 | child: GetBuilder( 60 | builder: buildWidget, 61 | assignId: true, 62 | )); 63 | } 64 | 65 | Widget buildWidget(T controller); 66 | } 67 | 68 | ``` 69 | 70 | #### BaseController 71 | BaseController继承于get的SuperController,因此,SuperController拥有的关于app的生命周期回调BaseController一样拥有,同时还有上面BasePage拥有的关于路由页面的生命周期回调,不但如此,还提供eventBus事件监听功能,可以在不同的BaseController之间进行基于eventBus的事件通信。只需要复写getListenEvent方法返回你需要监听的event事件类型,当其他地方发送监听的事件后,就会通过onReceiveEvent方法回调给当前的BaseController,类似这样: 72 | ``` dart 73 | class HomePageController extends BaseController { 74 | @override 75 | List getListenEvent() { 76 | return [CustomTextEvent]; 77 | } 78 | 79 | @override 80 | void onReceiveEvent(event) { 81 | switch (event.runtimeType) { 82 | case CustomTextEvent: 83 | Toast.show(event.text); 84 | break; 85 | default: 86 | } 87 | } 88 | ``` 89 | 在onReady的时候BaseController会自动帮你注册getListenEvent的需要关注的事件,同时在页面onClose的时候会自动接注册eventBus的事件监听,不需要手动处理。 90 | 91 | ### 页面路由表生成 92 | 因为用到get的路由功能,为了避免手写路由降低效率,这里用到了我写的另外一个开源库 `getx_route_generator`,只需要在BasePage上面添加`GetXRoutePage`注解然后运行命令行指令就可以生成一份路由表。生成的代码如下: 93 | ``` dart 94 | // ignore_for_file: constant_identifier_names 95 | import 'package:get/get.dart'; 96 | import 'package:flutter_quick_template/page/home_page.dart'; 97 | 98 | class RouteTable { 99 | static const String home = '/home'; 100 | 101 | static final List pages = [ 102 | GetPage(name: '/home', page: () => HomePage()), 103 | ]; 104 | } 105 | 106 | ``` 107 | 详情可以了解下:[getx_route_generator](https://pub.dev/packages/getx_route_generator) 108 | 109 | 110 | ### 轻量级存储 111 | 在App的开发过程中,我们经常需要保存一些key-value的持久化数据或者配置,在Android中经常用到的就是shared_preferences,而ios对应的就是NSUserDefaults 112 | ,本项目提供了基于shared_preferences和MMKV的封装类,开箱即用,根据需要选择即可,用法: 113 | ``` dart 114 | LightStorage.getString(key,defaultValue) 115 | LightStorage.clear() 116 | LightStorage.putString(key,defaultValue) 117 | LightStorage.containsKey(key) 118 | LightStorage.remove(key) 119 | ``` 120 | 121 | ### Utils工具类 122 | 本项目提供常用的三种工具类,一个是loading,toast,和log,之所以封装起来,是为了隔离依赖,如果你的项目需要定制或者不一样的底层实现,就可以很方便的切换而不需要修改很多的代码。 123 | 124 | #### Loading 125 | ``` dart 126 | Loading.show() 127 | Loading.dismiss() 128 | ``` 129 | 130 | #### toast 131 | ``` dart 132 | Toast.show() 133 | ``` 134 | 135 | 136 | #### log 137 | ``` dart 138 | logV("log msg") 139 | 140 | logD("log msg") 141 | 142 | logI("log msg") 143 | 144 | logW("log msg") 145 | 146 | logE("log msg") 147 | 148 | logWTF("log msg") 149 | 150 | ``` 151 | ### Extensions 152 | #### EventBus 153 | 可以直接发送: 154 | ``` dart 155 | CustomTextEvent(text: "msg from EventTestPage").sendEvent(); 156 | ``` 157 | #### GestureDetector 158 | ``` dart 159 | Text("onTab").onTab((){}); 160 | Text("onDoubleTab").onDoubleTab((){}); 161 | Text("onLongPress").onLongPress((){}); 162 | ``` 163 | #### Widgets 164 | 都调用styled_widget库的extensions方法,就像这样: 165 | ``` dart 166 | Icon(OMIcons.home, color: Colors.white) 167 | .padding(all: 10) 168 | .decorated(color: Color(0xff7AC1E7), shape: BoxShape.circle) 169 | .padding(all: 15) 170 | .decorated(color: Color(0xffE8F2F7), shape: BoxShape.circle) 171 | .padding(all: 20) 172 | .card( 173 | elevation: 10, 174 | shape: RoundedRectangleBorder( 175 | borderRadius: BorderRadius.circular(20), 176 | ), 177 | ) 178 | .alignment(Alignment.center) 179 | .backgroundColor(Color(0xffEBECF1)); 180 | ``` 181 | 更多用法可以查阅styled_widget的官网 182 | https://github.com/ReinBentdal/styled_widget 183 | 184 | 185 | ### Http 186 | Http请求是结合了dio和retrofit两个开源库实现的,如下: 187 | ``` dart 188 | Result result = await Http.client().getInfo(); 189 | Toast.show("${result.data}"); 190 | ``` 191 | 详情可以阅读源码 -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | android { 26 | namespace "com.example.flutter_quick_template" 27 | compileSdkVersion flutter.compileSdkVersion 28 | ndkVersion flutter.ndkVersion 29 | 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | 39 | sourceSets { 40 | main.java.srcDirs += 'src/main/kotlin' 41 | } 42 | 43 | defaultConfig { 44 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 45 | applicationId "com.example.flutter_quick_template" 46 | // You can update the following values to match your application needs. 47 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. 48 | minSdkVersion flutter.minSdkVersion 49 | targetSdkVersion flutter.targetSdkVersion 50 | versionCode flutterVersionCode.toInteger() 51 | versionName flutterVersionName 52 | } 53 | 54 | buildTypes { 55 | release { 56 | // TODO: Add your own signing config for the release build. 57 | // Signing with the debug keys for now, so `flutter run --release` works. 58 | signingConfig signingConfigs.debug 59 | } 60 | } 61 | } 62 | 63 | flutter { 64 | source '../..' 65 | } 66 | 67 | dependencies {} 68 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 14 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_quick_template/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_quick_template 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = "../build" 9 | subprojects { 10 | project.buildDir = "${rootProject.buildDir}/${project.name}" 11 | } 12 | subprojects { 13 | project.evaluationDependsOn(":app") 14 | } 15 | 16 | tasks.register("clean", Delete) { 17 | delete rootProject.buildDir 18 | } 19 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "8.2.1" apply false 22 | id "org.jetbrains.kotlin.android" version "1.9.20" apply false 23 | } 24 | 25 | include ":app" 26 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | flutter_quick_template 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | flutter_quick_template 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | CADisableMinimumFrameDurationOnPhone 45 | 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /lib/Initializer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:get/get_utils/src/platform/platform.dart'; 4 | import 'base/LightStorage/light_storage.dart'; 5 | 6 | /// 全局初始化器 7 | class Initializer { 8 | /// 初始化 9 | static Future init() async { 10 | WidgetsFlutterBinding.ensureInitialized(); 11 | await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); 12 | await LightStorage.getInstance().init(); 13 | setSystemUi(); 14 | } 15 | 16 | static void setSystemUi() { 17 | if (GetPlatform.isAndroid) { 18 | SystemUiOverlayStyle systemUiOverlayStyle = const SystemUiOverlayStyle( 19 | statusBarColor: Colors.transparent, 20 | statusBarBrightness: Brightness.light, 21 | statusBarIconBrightness: Brightness.dark, 22 | systemNavigationBarDividerColor: Colors.transparent, 23 | systemNavigationBarColor: Colors.white, 24 | systemNavigationBarIconBrightness: Brightness.dark, 25 | ); 26 | SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/base/LightStorage/light_storage.dart: -------------------------------------------------------------------------------- 1 | // import 'mmkv_light_storage.dart'; 2 | import 'shared_preference_light_storage.dart'; 3 | 4 | abstract class AbsLightStorage { 5 | Future init(); 6 | Future putInt(String key, int value); 7 | Future putBoolean(String key, bool value); 8 | Future putDouble(String key, double value); 9 | Future putString(String key, String value); 10 | 11 | int getInt(String key, {int defaultValue = 0}); 12 | bool getBoolean(String key, {bool defaultValue = false}); 13 | double getDouble(String key, {double defaultValue = 0}); 14 | String getString(String key, {String defaultValue = ""}); 15 | 16 | bool containsKey(String key); 17 | 18 | Future remove(String key); 19 | Future clear(); 20 | } 21 | 22 | class LightStorage extends AbsLightStorage { 23 | late AbsLightStorage poxyStorage; 24 | 25 | LightStorage._({required this.poxyStorage}); 26 | 27 | static LightStorage? _instance; 28 | 29 | static LightStorage getInstance() { 30 | //目前有2种实现 31 | // _instance ??= LightStorage._(poxyStorage: MMKVStorage()); 32 | _instance ??= LightStorage._(poxyStorage: SharePreferenceStorage()); 33 | return _instance!; 34 | } 35 | 36 | @override 37 | Future clear() { 38 | return poxyStorage.clear(); 39 | } 40 | 41 | @override 42 | bool getBoolean(String key, {bool defaultValue = false}) { 43 | return poxyStorage.getBoolean(key, defaultValue: defaultValue); 44 | } 45 | 46 | @override 47 | double getDouble(String key, {double defaultValue = 0}) { 48 | return poxyStorage.getDouble(key, defaultValue: defaultValue); 49 | } 50 | 51 | @override 52 | int getInt(String key, {int defaultValue = 0}) { 53 | return poxyStorage.getInt(key, defaultValue: defaultValue); 54 | } 55 | 56 | @override 57 | String getString(String key, {String defaultValue = ""}) { 58 | return poxyStorage.getString(key, defaultValue: defaultValue); 59 | } 60 | 61 | @override 62 | Future init() async { 63 | return poxyStorage.init(); 64 | } 65 | 66 | @override 67 | Future putBoolean(String key, bool value) { 68 | return poxyStorage.putBoolean(key, value); 69 | } 70 | 71 | @override 72 | Future putDouble(String key, double value) { 73 | return poxyStorage.putDouble(key, value); 74 | } 75 | 76 | @override 77 | Future putInt(String key, int value) { 78 | return poxyStorage.putInt(key, value); 79 | } 80 | 81 | @override 82 | Future putString(String key, String value) { 83 | return poxyStorage.putString(key, value); 84 | } 85 | 86 | @override 87 | Future remove(String key) { 88 | return poxyStorage.remove(key); 89 | } 90 | 91 | @override 92 | bool containsKey(String key) { 93 | return poxyStorage.containsKey(key); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /lib/base/LightStorage/mmkv_light_storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:mmkv/mmkv.dart'; 2 | import 'light_storage.dart'; 3 | 4 | class MMKVStorage extends AbsLightStorage { 5 | late MMKV _mmkv; 6 | @override 7 | Future clear() async { 8 | _mmkv.clearAll(); 9 | } 10 | 11 | @override 12 | bool getBoolean(String key, {bool defaultValue = false}) { 13 | return _mmkv.decodeBool(key, defaultValue: defaultValue); 14 | } 15 | 16 | @override 17 | double getDouble(String key, {double defaultValue = 0}) { 18 | return _mmkv.decodeDouble(key, defaultValue: defaultValue); 19 | } 20 | 21 | @override 22 | int getInt(String key, {int defaultValue = 0}) { 23 | return _mmkv.decodeInt(key, defaultValue: defaultValue); 24 | } 25 | 26 | @override 27 | String getString(String key, {String defaultValue = ""}) { 28 | return _mmkv.decodeString(key) ?? defaultValue; 29 | } 30 | 31 | @override 32 | Future init() async { 33 | await MMKV.initialize(); 34 | _mmkv = MMKV.defaultMMKV(); 35 | } 36 | 37 | @override 38 | Future putBoolean(String key, bool value) async { 39 | _mmkv.encodeBool(key, value); 40 | } 41 | 42 | @override 43 | Future putDouble(String key, double value) async { 44 | _mmkv.encodeDouble(key, value); 45 | } 46 | 47 | @override 48 | Future putInt(String key, int value) async { 49 | _mmkv.encodeInt(key, value); 50 | } 51 | 52 | @override 53 | Future putString(String key, String value) async { 54 | _mmkv.encodeString(key, value); 55 | } 56 | 57 | @override 58 | Future remove(String key) async { 59 | _mmkv.removeValue(key); 60 | } 61 | 62 | @override 63 | bool containsKey(String key) { 64 | return _mmkv.containsKey(key); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/base/LightStorage/shared_preference_light_storage.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | import 'light_storage.dart'; 3 | 4 | class SharePreferenceStorage extends AbsLightStorage { 5 | late SharedPreferences _preferences; 6 | @override 7 | Future init() async { 8 | _preferences = await SharedPreferences.getInstance(); 9 | } 10 | 11 | @override 12 | bool getBoolean(String key, {bool defaultValue = false}) { 13 | return _preferences.getBool(key) ?? defaultValue; 14 | } 15 | 16 | @override 17 | double getDouble(String key, {double defaultValue = 0}) { 18 | return _preferences.getDouble(key) ?? defaultValue; 19 | } 20 | 21 | @override 22 | int getInt(String key, {int defaultValue = 0}) { 23 | return _preferences.getInt(key) ?? defaultValue; 24 | } 25 | 26 | @override 27 | String getString(String key, {String defaultValue = ""}) { 28 | return _preferences.getString(key) ?? defaultValue; 29 | } 30 | 31 | @override 32 | Future putBoolean(String key, bool value) { 33 | return _preferences.setBool(key, value); 34 | } 35 | 36 | @override 37 | Future putDouble(String key, double value) { 38 | return _preferences.setDouble(key, value); 39 | } 40 | 41 | @override 42 | Future putInt(String key, int value) { 43 | return _preferences.setInt(key, value); 44 | } 45 | 46 | @override 47 | Future putString(String key, String value) { 48 | return _preferences.setString(key, value); 49 | } 50 | 51 | @override 52 | Future remove(String key) { 53 | return _preferences.remove(key); 54 | } 55 | 56 | @override 57 | Future clear() { 58 | return _preferences.clear(); 59 | } 60 | 61 | @override 62 | bool containsKey(String key) { 63 | return _preferences.containsKey(key); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/base/base_controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:get/get.dart'; 5 | 6 | import '../utils/event_bus.dart'; 7 | import 'page_life_state.dart'; 8 | 9 | class BaseController extends SuperController with PageLifeState { 10 | StreamSubscription? streamSubscription; 11 | @mustCallSuper 12 | @override 13 | void onReady() { 14 | super.onReady(); 15 | var listenerEventList = getListenEvent(); 16 | if (listenerEventList.isNotEmpty) { 17 | streamSubscription = 18 | EventBus().onTypes(listenerEventList).listen(onReceiveEvent); 19 | } 20 | } 21 | 22 | void onReceiveEvent(event) {} 23 | 24 | List getListenEvent() { 25 | return []; 26 | } 27 | 28 | @mustCallSuper 29 | @override 30 | void onClose() { 31 | super.onClose(); 32 | streamSubscription?.cancel(); 33 | } 34 | 35 | @override 36 | void onDetached() {} 37 | 38 | @override 39 | void onHidden() {} 40 | 41 | @override 42 | void onInactive() {} 43 | 44 | @override 45 | void onPaused() {} 46 | 47 | @override 48 | void onResumed() {} 49 | } 50 | -------------------------------------------------------------------------------- /lib/base/base_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:get/get.dart'; 3 | import 'package:lifecycle/lifecycle.dart'; 4 | 5 | import '../utils/log_util.dart'; 6 | import 'base_controller.dart'; 7 | 8 | abstract class BasePage extends GetView { 9 | BuildContext? context; 10 | 11 | T generateController(); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | this.context = context; 16 | Get.put(generateController()); 17 | return LifecycleWrapper( 18 | onLifecycleEvent: (LifecycleEvent lifecycleEvent) { 19 | logI("$runtimeType $lifecycleEvent"); 20 | switch (lifecycleEvent) { 21 | case LifecycleEvent.visible: 22 | controller.onPageVisible(); 23 | break; 24 | case LifecycleEvent.inactive: 25 | controller.onPageInActive(); 26 | break; 27 | case LifecycleEvent.active: 28 | controller.onPageActive(); 29 | break; 30 | case LifecycleEvent.invisible: 31 | controller.onPageInVisible(); 32 | break; 33 | default: 34 | } 35 | }, 36 | child: GetBuilder( 37 | builder: buildWidget, 38 | assignId: true, 39 | )); 40 | } 41 | 42 | Widget buildWidget(T controller); 43 | } 44 | -------------------------------------------------------------------------------- /lib/base/base_scaffold_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'base_controller.dart'; 4 | import 'base_page.dart'; 5 | 6 | abstract class BaseScaffoldPage extends BasePage { 7 | @override 8 | Widget buildWidget(BaseController controller) { 9 | return Scaffold( 10 | appBar: getAppBar(), 11 | body: getBody(), 12 | bottomNavigationBar: getBottomNavigationBar(), 13 | ); 14 | } 15 | 16 | PreferredSizeWidget? getAppBar(); 17 | 18 | Widget? getBody(); 19 | 20 | Widget? getBottomNavigationBar() { 21 | return null; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/base/network/api_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:retrofit/http.dart'; 3 | 4 | import 'result.dart'; 5 | import 'test_info.dart'; 6 | part 'api_client.g.dart'; 7 | 8 | @RestApi() 9 | abstract class ApiClient { 10 | factory ApiClient(Dio dio, {String baseUrl}) = _ApiClient; 11 | 12 | @GET("/info") 13 | Future> getInfo(); 14 | } 15 | -------------------------------------------------------------------------------- /lib/base/network/api_client.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'api_client.dart'; 4 | 5 | // ************************************************************************** 6 | // RetrofitGenerator 7 | // ************************************************************************** 8 | 9 | // ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers 10 | 11 | class _ApiClient implements ApiClient { 12 | _ApiClient( 13 | this._dio, { 14 | this.baseUrl, 15 | }); 16 | 17 | final Dio _dio; 18 | 19 | String? baseUrl; 20 | 21 | @override 22 | Future> getInfo() async { 23 | const _extra = {}; 24 | final queryParameters = {}; 25 | final _headers = {}; 26 | final Map? _data = null; 27 | final _result = await _dio 28 | .fetch>(_setStreamType>(Options( 29 | method: 'GET', 30 | headers: _headers, 31 | extra: _extra, 32 | ) 33 | .compose( 34 | _dio.options, 35 | '/info', 36 | queryParameters: queryParameters, 37 | data: _data, 38 | ) 39 | .copyWith( 40 | baseUrl: _combineBaseUrls( 41 | _dio.options.baseUrl, 42 | baseUrl, 43 | )))); 44 | final value = Result.fromJson( 45 | _result.data!, 46 | (json) => TestInfo.fromJson(json as Map), 47 | ); 48 | return value; 49 | } 50 | 51 | RequestOptions _setStreamType(RequestOptions requestOptions) { 52 | if (T != dynamic && 53 | !(requestOptions.responseType == ResponseType.bytes || 54 | requestOptions.responseType == ResponseType.stream)) { 55 | if (T == String) { 56 | requestOptions.responseType = ResponseType.plain; 57 | } else { 58 | requestOptions.responseType = ResponseType.json; 59 | } 60 | } 61 | return requestOptions; 62 | } 63 | 64 | String _combineBaseUrls( 65 | String dioBaseUrl, 66 | String? baseUrl, 67 | ) { 68 | if (baseUrl == null || baseUrl.trim().isEmpty) { 69 | return dioBaseUrl; 70 | } 71 | 72 | final url = Uri.parse(baseUrl); 73 | 74 | if (url.isAbsolute) { 75 | return url.toString(); 76 | } 77 | 78 | return Uri.parse(dioBaseUrl).resolveUri(url).toString(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/base/network/api_exception.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dio/dio.dart'; 4 | import 'package:get/utils.dart'; 5 | 6 | import '../../generated/locales.g.dart'; 7 | 8 | class ApiException extends CustomDioError { 9 | static String CLIENT_NET_ERROR = LocaleKeys.network_network_error.tr; 10 | static String SERVER_NET_ERROR = LocaleKeys.network_system_error.tr; 11 | static String NET_CONNECT_ERROR = LocaleKeys.network_net_disconnect.tr; 12 | 13 | static const CODE_SUCCESS = 0; 14 | static const CODE_UNKNOWN = 1000; 15 | 16 | final int code; 17 | final dynamic cause; 18 | final String? _message; 19 | 20 | ApiException({ 21 | required this.code, 22 | String? message, 23 | this.cause, 24 | }) : _message = message; 25 | 26 | @override 27 | String get message => _message ?? cause?.toString() ?? ''; 28 | 29 | @override 30 | String toString() { 31 | var str = 'ApiException{code: $code, message: $message, cause: $cause}'; 32 | if (cause != null && cause is Error) { 33 | str += '\n${(cause as Error).stackTrace}'; 34 | } 35 | return str; 36 | } 37 | 38 | factory ApiException.create(DioException error) { 39 | switch (error.type) { 40 | case DioExceptionType.cancel: 41 | return BadRequestException(-1, LocaleKeys.network_request_cancel); 42 | case DioExceptionType.connectionTimeout: 43 | case DioExceptionType.sendTimeout: 44 | return BadRequestException(-1, CLIENT_NET_ERROR); 45 | case DioExceptionType.receiveTimeout: 46 | return BadRequestException(-1, SERVER_NET_ERROR); 47 | case DioExceptionType.badResponse: 48 | try { 49 | int errCode = error.response!.statusCode!; 50 | switch (errCode) { 51 | case 400: 52 | return BadRequestException(errCode, SERVER_NET_ERROR); 53 | case 401: 54 | case 403: 55 | case 404: 56 | case 405: 57 | case 500: 58 | case 502: 59 | case 503: 60 | case 505: 61 | return UnauthorisedException(errCode, SERVER_NET_ERROR); 62 | default: 63 | return ApiException( 64 | code: errCode, message: error.response!.statusMessage!); 65 | } 66 | } on Exception catch (_) { 67 | return ApiException(code: -1, message: NET_CONNECT_ERROR); 68 | } 69 | case DioExceptionType.unknown: 70 | if (error.error is SocketException) { 71 | return ApiException(code: -1, message: CLIENT_NET_ERROR); 72 | } else { 73 | return ApiException(code: -1, message: NET_CONNECT_ERROR); 74 | } 75 | default: 76 | return ApiException(code: -1, message: NET_CONNECT_ERROR); 77 | } 78 | } 79 | 80 | @override 81 | DioException copyWith( 82 | {RequestOptions? requestOptions, 83 | Response? response, 84 | DioExceptionType? type, 85 | Object? error, 86 | StackTrace? stackTrace, 87 | String? message}) { 88 | throw UnimplementedError(); 89 | } 90 | 91 | @override 92 | DioExceptionReadableStringBuilder? stringBuilder; 93 | } 94 | 95 | class BadRequestException extends ApiException { 96 | BadRequestException([code, message]) : super(code: code, message: message); 97 | } 98 | 99 | class UnauthorisedException extends ApiException { 100 | UnauthorisedException([code, message]) : super(code: code, message: message); 101 | } 102 | 103 | abstract class CustomDioError implements DioException { 104 | static final defaultErrorRequestOptions = RequestOptions(path: ''); 105 | 106 | @override 107 | String get message; 108 | 109 | @override 110 | dynamic error; 111 | 112 | RequestOptions _requestOptions = defaultErrorRequestOptions; 113 | 114 | @override 115 | RequestOptions get requestOptions => _requestOptions; 116 | 117 | @override 118 | set requestOptions(RequestOptions value) { 119 | _requestOptions = value; 120 | } 121 | 122 | @override 123 | Response? response; 124 | 125 | @override 126 | DioExceptionType type = DioExceptionType.unknown; 127 | 128 | @override 129 | StackTrace get stackTrace => StackTrace.current; 130 | } 131 | -------------------------------------------------------------------------------- /lib/base/network/api_result_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | import 'api_exception.dart'; 4 | 5 | class ApiResultInterceptor extends Interceptor { 6 | @override 7 | Future onResponse( 8 | Response resp, ResponseInterceptorHandler handler) async { 9 | final result = resp.data as Map; 10 | if (result["code"] == 10000) { 11 | handler.next(resp); 12 | return; 13 | } else { 14 | handler.reject( 15 | ApiException(code: result["code"], message: result["msg"]) 16 | ..requestOptions = resp.requestOptions, 17 | true); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/base/network/error_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'api_exception.dart'; 3 | 4 | class ErrorInterceptor extends Interceptor { 5 | @override 6 | void onError(DioException err, ErrorInterceptorHandler handler) { 7 | var apiException = err; 8 | 9 | if (err is! ApiException) { 10 | apiException = ApiException.create(err); 11 | } 12 | 13 | super.onError(apiException, handler); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/base/network/http.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:pretty_dio_logger/pretty_dio_logger.dart'; 3 | import 'api_client.dart'; 4 | import 'api_result_interceptor.dart'; 5 | import 'error_interceptor.dart'; 6 | import 'mock_interceptor.dart'; 7 | 8 | class Http { 9 | static const Duration connectTimeout = Duration(milliseconds: 20 * 1000); 10 | static const Duration receiveTimeout = Duration(milliseconds: 20 * 1000); 11 | //change to the real url 12 | static const BASE_URL = "https://xxx.com"; 13 | static final Http _instance = Http._internal(); 14 | 15 | factory Http() => _instance; 16 | 17 | late Dio dio; 18 | late ApiClient _client; 19 | 20 | static ApiClient client() { 21 | return Http()._client; 22 | } 23 | 24 | Http._internal() { 25 | BaseOptions options = BaseOptions( 26 | connectTimeout: connectTimeout, 27 | persistentConnection: true, 28 | receiveTimeout: receiveTimeout, 29 | baseUrl: BASE_URL, 30 | contentType: Headers.jsonContentType, 31 | responseType: ResponseType.json, 32 | receiveDataWhenStatusError: false, 33 | ); 34 | 35 | dio = Dio(options); 36 | //mock request ,if you want to requet you own service.must be remove 37 | dio.interceptors.add(MockInterceptor()); 38 | dio.interceptors.add(ApiResultInterceptor()); 39 | dio.interceptors.add(PrettyDioLogger(requestHeader: true)); 40 | dio.interceptors.add(ErrorInterceptor()); 41 | _client = ApiClient(dio, baseUrl: BASE_URL); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/base/network/mock_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:dio/dio.dart'; 4 | 5 | class MockInterceptor extends Interceptor { 6 | String jsonString = 7 | ''' 8 | { 9 | "code": 10000, 10 | "msg": "success", 11 | "data": {"age":22} 12 | } 13 | '''; 14 | 15 | @override 16 | void onRequest(RequestOptions options, RequestInterceptorHandler handler) { 17 | handler.resolve( 18 | Response(requestOptions: options, data: jsonDecode(jsonString)), true); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/base/network/result.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'api_exception.dart'; 3 | part 'result.g.dart'; 4 | 5 | @JsonSerializable(genericArgumentFactories: true) 6 | class Result { 7 | int code; 8 | T? data; 9 | String? msg; 10 | 11 | Result({required this.code, this.data, this.msg}); 12 | 13 | factory Result.fromJson( 14 | Map json, 15 | T Function(dynamic json) fromJsonT, 16 | ) => 17 | _$ResultFromJson(json, fromJsonT); 18 | 19 | Map toJson(Object? Function(T value) toJsonT) => 20 | _$ResultToJson(this, toJsonT); 21 | 22 | factory Result.fromMapJson(Map json) { 23 | return Result( 24 | code: json['code'] as int, 25 | data: json['data'] as T?, 26 | msg: json['msg'] as String?, 27 | ); 28 | } 29 | 30 | ApiException toException({dynamic cause}) => 31 | ApiException(code: code, message: msg, cause: cause); 32 | } 33 | -------------------------------------------------------------------------------- /lib/base/network/result.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'result.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Result _$ResultFromJson( 10 | Map json, 11 | T Function(Object? json) fromJsonT, 12 | ) => 13 | Result( 14 | code: json['code'] as int, 15 | data: _$nullableGenericFromJson(json['data'], fromJsonT), 16 | msg: json['msg'] as String?, 17 | ); 18 | 19 | Map _$ResultToJson( 20 | Result instance, 21 | Object? Function(T value) toJsonT, 22 | ) => 23 | { 24 | 'code': instance.code, 25 | 'data': _$nullableGenericToJson(instance.data, toJsonT), 26 | 'msg': instance.msg, 27 | }; 28 | 29 | T? _$nullableGenericFromJson( 30 | Object? input, 31 | T Function(Object? json) fromJson, 32 | ) => 33 | input == null ? null : fromJson(input); 34 | 35 | Object? _$nullableGenericToJson( 36 | T? input, 37 | Object? Function(T value) toJson, 38 | ) => 39 | input == null ? null : toJson(input); 40 | -------------------------------------------------------------------------------- /lib/base/network/test_info.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs, sort_constructors_first 2 | import 'package:json_annotation/json_annotation.dart'; 3 | 4 | part 'test_info.g.dart'; 5 | 6 | @JsonSerializable() 7 | class TestInfo { 8 | int age; 9 | String name; 10 | TestInfo({ 11 | this.age = 0, 12 | this.name = '', 13 | }); 14 | 15 | factory TestInfo.fromJson(Map json) => 16 | _$TestInfoFromJson(json); 17 | Map toJson() => _$TestInfoToJson(this); 18 | 19 | TestInfo copyWith({ 20 | int? age, 21 | String? name, 22 | }) { 23 | return TestInfo( 24 | age: age ?? this.age, 25 | name: name ?? this.name, 26 | ); 27 | } 28 | 29 | Map toMap() { 30 | return { 31 | 'age': age, 32 | 'name': name, 33 | }; 34 | } 35 | 36 | @override 37 | String toString() => 'TestInfo(age: $age, name: $name)'; 38 | 39 | @override 40 | bool operator ==(covariant TestInfo other) { 41 | if (identical(this, other)) return true; 42 | 43 | return 44 | other.age == age && 45 | other.name == name; 46 | } 47 | 48 | @override 49 | int get hashCode => age.hashCode ^ name.hashCode; 50 | } 51 | -------------------------------------------------------------------------------- /lib/base/network/test_info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'test_info.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | TestInfo _$TestInfoFromJson(Map json) => TestInfo( 10 | age: json['age'] as int? ?? 0, 11 | name: json['name'] as String? ?? '', 12 | ); 13 | 14 | Map _$TestInfoToJson(TestInfo instance) => { 15 | 'age': instance.age, 16 | 'name': instance.name, 17 | }; 18 | -------------------------------------------------------------------------------- /lib/base/page_life_state.dart: -------------------------------------------------------------------------------- 1 | mixin PageLifeState { 2 | void onPageVisible() {} 3 | 4 | void onPageActive() {} 5 | 6 | void onPageInActive() {} 7 | 8 | void onPageInVisible() {} 9 | } 10 | -------------------------------------------------------------------------------- /lib/base/widgets/net_image.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | 5 | class NetImage extends StatelessWidget { 6 | final String imageUrl; 7 | 8 | final Widget? placeholder; 9 | 10 | final Widget? errorWidget; 11 | 12 | final BoxFit? fit; 13 | 14 | final double? width; 15 | 16 | final double? height; 17 | 18 | final double? placeHolderHeight; 19 | 20 | const NetImage( 21 | {Key? key, 22 | required this.imageUrl, 23 | this.placeholder, 24 | this.errorWidget, 25 | this.fit, 26 | this.width, 27 | this.height, 28 | this.placeHolderHeight}) 29 | : super(key: key); 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | return CachedNetworkImage( 34 | imageUrl: imageUrl, 35 | placeholder: (context, url) => placeholder ?? _defaultPlaceholder(), 36 | errorWidget: (context, url, error) => 37 | errorWidget ?? _defaultPlaceholder(), 38 | fit: fit ?? BoxFit.cover, 39 | width: width, 40 | height: height, 41 | fadeInDuration: const Duration(milliseconds: 1), 42 | fadeOutDuration: const Duration(milliseconds: 1), 43 | ); 44 | } 45 | 46 | Widget _defaultPlaceholder() { 47 | return Container( 48 | width: placeHolderHeight, 49 | height: height ?? 300.h, 50 | decoration: BoxDecoration( 51 | color: const Color.fromRGBO(231, 234, 236, 1), 52 | borderRadius: BorderRadius.all( 53 | Radius.circular(5.h), 54 | ), 55 | )); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/events/text_event.dart: -------------------------------------------------------------------------------- 1 | class CustomTextEvent { 2 | CustomTextEvent({required this.text}); 3 | String text = ""; 4 | } 5 | -------------------------------------------------------------------------------- /lib/generated/locales.g.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:get_cli/get_cli.dart 2 | 3 | // ignore_for_file: lines_longer_than_80_chars 4 | // ignore: avoid_classes_with_only_static_members 5 | class AppTranslation { 6 | static Map> translations = { 7 | 'en_US': Locales.en_US, 8 | 'zh_CN': Locales.zh_CN, 9 | }; 10 | } 11 | 12 | class LocaleKeys { 13 | LocaleKeys._(); 14 | static const network_network_error = 'network_network_error'; 15 | static const network_system_error = 'network_system_error'; 16 | static const network_net_disconnect = 'network_net_disconnect'; 17 | static const network_request_cancel = 'network_request_cancel'; 18 | } 19 | 20 | class Locales { 21 | static const en_US = { 22 | 'network_network_error': 23 | 'Network abnormality, please check the network connection status.', 24 | 'network_system_error': 'The system is busy, please try again later!', 25 | 'network_net_disconnect': 26 | 'Network not connected, please check and try again.', 27 | 'network_request_cancel': 'request canceled', 28 | }; 29 | static const zh_CN = { 30 | 'network_network_error': '网络异常,请检网络连接状态', 31 | 'network_system_error': '系统繁忙,请稍后再试!', 32 | 'network_net_disconnect': '网络未连接,请检查后重试', 33 | 'network_request_cancel': '请求取消', 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /lib/generated/route_table.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: constant_identifier_names 2 | import 'package:get/get.dart'; 3 | import 'package:flutter_quick_template/page/event_test_page.dart'; 4 | import 'package:flutter_quick_template/page/home_page.dart'; 5 | import 'package:flutter_quick_template/page/http_page.dart'; 6 | import 'package:flutter_quick_template/page/light_storage_page.dart'; 7 | 8 | class RouteTable { 9 | static const String event = '/event'; 10 | static const String home = '/home'; 11 | static const String http = '/http'; 12 | static const String lightStorage = '/lightStorage'; 13 | 14 | static final List pages = [ 15 | GetPage(name: '/event', page: () => EventTestPage()), 16 | GetPage(name: '/home', page: () => HomePage()), 17 | GetPage(name: '/http', page: () => HttpPage()), 18 | GetPage(name: '/lightStorage', page: () => LightStoragePage()), 19 | ]; 20 | } 21 | 22 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/rendering.dart'; 4 | import 'package:flutter_localizations/flutter_localizations.dart'; 5 | import 'package:flutter_quick_template/generated/route_table.dart'; 6 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 7 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; 8 | import 'package:get/get.dart'; 9 | import 'package:lifecycle/lifecycle.dart'; 10 | 11 | import 'Initializer.dart'; 12 | import 'generated/locales.g.dart'; 13 | import 'utils/log_util.dart'; 14 | 15 | reportError(FlutterErrorDetails error) {} 16 | 17 | void main() async { 18 | var onError = FlutterError.onError; 19 | FlutterError.onError = (FlutterErrorDetails details) { 20 | onError?.call(details); 21 | reportError(details); 22 | }; 23 | PlatformDispatcher.instance.onError = (error, stack) { 24 | logE(error); 25 | return true; 26 | }; 27 | await Initializer.init(); 28 | runApp(const MyApp()); 29 | } 30 | 31 | class MyApp extends StatelessWidget { 32 | const MyApp({Key? key}) : super(key: key); 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | debugPaintSizeEnabled = false; 37 | return ScreenUtilInit( 38 | designSize: const Size(375, 812), 39 | minTextAdapt: true, 40 | splitScreenMode: true, 41 | builder: (BuildContext context, Widget? child) => GetMaterialApp( 42 | getPages: RouteTable.pages, 43 | localizationsDelegates: const [ 44 | GlobalMaterialLocalizations.delegate, 45 | GlobalWidgetsLocalizations.delegate, 46 | GlobalCupertinoLocalizations.delegate, //iOS 47 | ], 48 | translationsKeys: AppTranslation.translations, 49 | defaultTransition: Transition.cupertino, 50 | supportedLocales: const [ 51 | Locale('zh', 'CN'), 52 | Locale('en', 'US'), 53 | ], 54 | locale: const Locale('zh', 'CN'), 55 | fallbackLocale: const Locale('zh', 'CN'), 56 | navigatorObservers: [ 57 | defaultLifecycleObserver, 58 | FlutterSmartDialog.observer 59 | ], 60 | debugShowCheckedModeBanner: false, 61 | theme: ThemeData.fallback(useMaterial3: true), 62 | themeMode: ThemeMode.light, 63 | initialRoute: RouteTable.home, 64 | routingCallback: (routing) {}, 65 | builder: FlutterSmartDialog.init(), 66 | )); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/page/event_test_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:getx_route_annotations/getx_route_annotations.dart'; 3 | import '../utils/global_extension.dart'; 4 | import 'package:get/get.dart'; 5 | 6 | import '../base/base_controller.dart'; 7 | import '../base/base_scaffold_page.dart'; 8 | import '../events/text_event.dart'; 9 | 10 | @GetXRoutePage("/event") 11 | class EventTestPage extends BaseScaffoldPage { 12 | @override 13 | EventTestController generateController() { 14 | return EventTestController(); 15 | } 16 | 17 | @override 18 | PreferredSizeWidget? getAppBar() { 19 | return AppBar( 20 | leading: const Icon(Icons.arrow_back).onTab(() { 21 | Get.back(); 22 | }), 23 | title: const Text("EventTestPage"), 24 | ); 25 | } 26 | 27 | @override 28 | Widget? getBody() { 29 | return ElevatedButton( 30 | onPressed: () { 31 | CustomTextEvent(text: "msg from EventTestPage").sendEvent(); 32 | }, 33 | child: const Text("send Event to HomePage")) 34 | .align(Alignment.center); 35 | } 36 | } 37 | 38 | class EventTestController extends BaseController { 39 | @override 40 | void onReady() { 41 | super.onReady(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/page/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_quick_template/generated/route_table.dart'; 3 | import 'package:flutter_quick_template/utils/global_extension.dart'; 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 5 | import 'package:get/get.dart'; 6 | import 'package:getx_route_annotations/getx_route_annotations.dart'; 7 | import '../base/base_controller.dart'; 8 | import '../base/base_scaffold_page.dart'; 9 | import '../events/text_event.dart'; 10 | import '../utils/loading_util.dart'; 11 | import '../utils/log_util.dart'; 12 | import '../utils/toast_util.dart'; 13 | 14 | @GetXRoutePage("/home") 15 | class HomePage extends BaseScaffoldPage { 16 | @override 17 | HomePageController generateController() { 18 | return HomePageController(); 19 | } 20 | 21 | @override 22 | PreferredSizeWidget? getAppBar() { 23 | return AppBar( 24 | title: const Text("flutter_quick_template"), 25 | ); 26 | } 27 | 28 | @override 29 | Widget? getBody() { 30 | return Column( 31 | mainAxisAlignment: MainAxisAlignment.center, 32 | crossAxisAlignment: CrossAxisAlignment.start, 33 | children: [ 34 | ElevatedButton( 35 | onPressed: () async { 36 | Loading.show(); 37 | await Future.delayed(2.seconds); 38 | Loading.dismiss(); 39 | }, 40 | child: const Text("Loading example")) 41 | .marginOnly(bottom: 10.h), 42 | ElevatedButton( 43 | onPressed: () async { 44 | Toast.show("test toast"); 45 | }, 46 | child: const Text("Toast example")) 47 | .marginOnly(bottom: 10.h), 48 | ElevatedButton( 49 | onPressed: () async { 50 | Get.toNamed(RouteTable.event); 51 | }, 52 | child: const Text("Event example")) 53 | .marginOnly(bottom: 10.h), 54 | ElevatedButton( 55 | onPressed: () async { 56 | Get.toNamed(RouteTable.http); 57 | }, 58 | child: const Text("Http mock example")) 59 | .marginOnly(bottom: 10.h), 60 | ElevatedButton( 61 | onPressed: () async { 62 | Get.toNamed(RouteTable.lightStorage); 63 | }, 64 | child: const Text("lightStorage example")) 65 | .marginOnly(bottom: 10.h), 66 | ], 67 | ).align(Alignment.center); 68 | } 69 | } 70 | 71 | class HomePageController extends BaseController { 72 | @override 73 | List getListenEvent() { 74 | return [CustomTextEvent]; 75 | } 76 | 77 | @override 78 | void onReceiveEvent(event) { 79 | switch (event.runtimeType) { 80 | case CustomTextEvent: 81 | Toast.show(event.text); 82 | break; 83 | default: 84 | } 85 | } 86 | 87 | @override 88 | void onClose() { 89 | super.onClose(); 90 | logI("onClose"); 91 | } 92 | 93 | @override 94 | void onPageActive() { 95 | super.onPageActive(); 96 | logI("$runtimeType onPageActive"); 97 | } 98 | 99 | @override 100 | void onPageInVisible() { 101 | super.onPageInVisible(); 102 | logI("$runtimeType onPageInVisible"); 103 | } 104 | 105 | @override 106 | void onPageVisible() { 107 | super.onPageVisible(); 108 | logI("$runtimeType onPageVisible"); 109 | } 110 | 111 | @override 112 | void onPageInActive() { 113 | super.onPageInActive(); 114 | logI("$runtimeType onPageInActive"); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /lib/page/http_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_quick_template/utils/global_extension.dart'; 3 | import 'package:get/get.dart'; 4 | import 'package:getx_route_annotations/getx_route_annotations.dart'; 5 | import '../base/base_controller.dart'; 6 | import '../base/base_scaffold_page.dart'; 7 | import '../base/network/http.dart'; 8 | import '../base/network/result.dart'; 9 | import '../base/network/test_info.dart'; 10 | import '../utils/toast_util.dart'; 11 | 12 | @GetXRoutePage("/http") 13 | class HttpPage extends BaseScaffoldPage { 14 | @override 15 | BaseController generateController() { 16 | return HttpPageController(); 17 | } 18 | 19 | @override 20 | PreferredSizeWidget? getAppBar() { 21 | return AppBar( 22 | leading: const Icon(Icons.arrow_back).onTab(() { 23 | Get.back(); 24 | }), 25 | title: const Text("HttpMockPage"), 26 | ); 27 | } 28 | 29 | @override 30 | Widget? getBody() { 31 | return ElevatedButton( 32 | onPressed: () async { 33 | Result result = await Http.client().getInfo(); 34 | Toast.show("${result.data}"); 35 | }, 36 | child: const Text("click to get Http result")) 37 | .align(Alignment.center); 38 | } 39 | } 40 | 41 | class HttpPageController extends BaseController {} 42 | -------------------------------------------------------------------------------- /lib/page/light_storage_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_quick_template/utils/global_extension.dart'; 3 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 | import 'package:get/get.dart'; 5 | import 'package:getx_route_annotations/getx_route_annotations.dart'; 6 | 7 | import '../base/LightStorage/light_storage.dart'; 8 | import '../base/base_controller.dart'; 9 | import '../base/base_scaffold_page.dart'; 10 | import '../utils/toast_util.dart'; 11 | 12 | @GetXRoutePage("/lightStorage") 13 | class LightStoragePage extends BaseScaffoldPage { 14 | String KEY = "key_save"; 15 | TextEditingController textController = TextEditingController(); 16 | 17 | @override 18 | BaseController generateController() { 19 | return LIghtStoragePageController(); 20 | } 21 | 22 | @override 23 | PreferredSizeWidget? getAppBar() { 24 | return AppBar( 25 | leading: const Icon(Icons.arrow_back).onTab(() { 26 | Get.back(); 27 | }), 28 | title: const Text("LightStorage"), 29 | ); 30 | } 31 | 32 | @override 33 | Widget? getBody() { 34 | return Column( 35 | mainAxisAlignment: MainAxisAlignment.center, 36 | crossAxisAlignment: CrossAxisAlignment.center, 37 | children: [ 38 | Row( 39 | mainAxisAlignment: MainAxisAlignment.center, 40 | children: [ 41 | TextField( 42 | controller: textController, 43 | ).sizedBox(width: 100.w), 44 | ElevatedButton( 45 | onPressed: () { 46 | LightStorage.getInstance() 47 | .putString(KEY, textController.text); 48 | }, 49 | child: const Text("save")) 50 | ], 51 | ).marginOnly(bottom: 10.h), 52 | ElevatedButton( 53 | onPressed: () { 54 | Toast.show(LightStorage.getInstance().getString(KEY)); 55 | }, 56 | child: const Text("getSaveContent")) 57 | ]).align(Alignment.center); 58 | } 59 | } 60 | 61 | class LIghtStoragePageController extends BaseController {} 62 | -------------------------------------------------------------------------------- /lib/utils/event_bus.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | class EventBus { 4 | factory EventBus() => _getInstance(); 5 | 6 | static EventBus _getInstance() { 7 | _instance ??= EventBus.normal(); 8 | return _instance!; 9 | } 10 | 11 | static EventBus? _instance; 12 | 13 | StreamController _streamController; 14 | 15 | /// Controller for the event bus stream. 16 | StreamController get streamController => _streamController; 17 | 18 | /// Creates an [EventBus]. 19 | /// 20 | /// If [sync] is true, events are passed directly to the stream's listeners 21 | /// during a [fire] call. If false (the default), the event will be passed to 22 | /// the listeners at a later time, after the code creating the event has 23 | /// completed. 24 | EventBus.normal({bool sync = false}) 25 | : _streamController = StreamController.broadcast(sync: sync); 26 | 27 | /// Instead of using the default [StreamController] you can use this constructor 28 | /// to pass your own controller. 29 | /// 30 | /// An example would be to use an RxDart Subject as the controller. 31 | EventBus.customController(StreamController controller) 32 | : _streamController = controller; 33 | 34 | /// Listens for events of Type [T] and its subtypes. 35 | /// 36 | /// The method is called like this: myEventBus.on(); 37 | /// 38 | /// If the method is called without a type parameter, the [Stream] contains every 39 | /// event of this [EventBus]. 40 | /// 41 | /// The returned [Stream] is a broadcast stream so multiple subscriptions are 42 | /// allowed. 43 | /// 44 | /// Each listener is handled independently, and if they pause, only the pausing 45 | /// listener is affected. A paused listener will buffer events internally until 46 | /// unpaused or canceled. So it's usually better to just cancel and later 47 | /// subscribe again (avoids memory leak). 48 | /// 49 | Stream on() { 50 | if (T == dynamic) { 51 | return streamController.stream as Stream; 52 | } else { 53 | return streamController.stream.where((event) => event is T).cast(); 54 | } 55 | } 56 | 57 | Stream onTypes(List types) { 58 | if (types.isEmpty) { 59 | return streamController.stream; 60 | } else { 61 | return streamController.stream 62 | .where((event) => types.any((type) => event.runtimeType == type)); 63 | } 64 | } 65 | 66 | /// Fires a new event on the event bus with the specified [event]. 67 | /// 68 | void fire(event) { 69 | streamController.add(event); 70 | } 71 | 72 | /// Destroy this [EventBus]. This is generally only in a testing context. 73 | /// 74 | void destroy() { 75 | _streamController.close(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/utils/global_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/gestures.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'event_bus.dart'; 4 | 5 | extension ObjectExtension on Object { 6 | void sendEvent({T? event}) { 7 | if (event == null) { 8 | EventBus().fire(this); 9 | return; 10 | } 11 | EventBus().fire(event); 12 | } 13 | } 14 | 15 | extension GestureExtension on Widget { 16 | GestureDetector onTab(GestureTapCallback tapCallback) { 17 | return GestureDetector(onTap: tapCallback, child: this); 18 | } 19 | 20 | GestureDetector onDoubleTab(GestureDoubleTapCallback tapCallback) { 21 | return GestureDetector(onDoubleTap: tapCallback, child: this); 22 | } 23 | 24 | GestureDetector onLongPress(GestureLongPressCallback tapCallback) { 25 | return GestureDetector(onLongPress: tapCallback, child: this); 26 | } 27 | } 28 | 29 | extension WidgetsExtension on Widget { 30 | Flexible flexible() { 31 | return Flexible(child: this); 32 | } 33 | 34 | Expanded expanded() { 35 | return Expanded(child: this); 36 | } 37 | 38 | SizedBox sizedBox({double? width, double? height}) { 39 | return SizedBox( 40 | width: width, 41 | height: height, 42 | child: this, 43 | ); 44 | } 45 | 46 | Align align(Alignment alignment, 47 | {double? widthFactor, double? heightFactor}) { 48 | return Align( 49 | alignment: alignment, 50 | widthFactor: widthFactor, 51 | heightFactor: heightFactor, 52 | child: this, 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/utils/loading_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; 2 | 3 | class Loading { 4 | static void show( 5 | {String msg = 'loading...', 6 | bool clickMaskDismiss = false, 7 | Duration? displayTime}) async { 8 | await SmartDialog.showLoading( 9 | msg: msg, 10 | clickMaskDismiss: clickMaskDismiss, 11 | displayTime: displayTime, 12 | usePenetrate: false); 13 | } 14 | 15 | static void dismiss() async { 16 | await SmartDialog.dismiss(status: SmartStatus.loading); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/utils/log_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | import 'dart:developer'; 3 | 4 | bool isDebug = const bool.fromEnvironment('dart.vm.product') != true; 5 | Logger _logger = Logger( 6 | printer: PrettyPrinter( 7 | stackTraceBeginIndex: 1, printTime: true, noBoxingByDefault: false), 8 | ); 9 | 10 | logV(dynamic msg) { 11 | if (isDebug) { 12 | _logger.t("$msg"); 13 | } 14 | } 15 | 16 | logD(dynamic msg) { 17 | if (isDebug) { 18 | _logger.d("$msg"); 19 | } 20 | } 21 | 22 | logI(dynamic msg) { 23 | if (isDebug) { 24 | _logger.i("$msg"); 25 | } 26 | } 27 | 28 | logW(dynamic msg) { 29 | if (isDebug) { 30 | _logger.w("$msg"); 31 | } 32 | } 33 | 34 | logE(dynamic msg) { 35 | if (isDebug) { 36 | _logger.e("$msg"); 37 | } 38 | } 39 | 40 | logWTF(dynamic msg) { 41 | if (isDebug) { 42 | log(msg); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/utils/toast_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; 2 | 3 | class Toast { 4 | static show(String msg, {Duration? displayTime}) { 5 | SmartDialog.showToast(msg, displayTime: displayTime); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.10) 3 | project(runner LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "flutter_quick_template") 8 | # The unique GTK application identifier for this application. See: 9 | # https://wiki.gnome.org/HowDoI/ChooseApplicationID 10 | set(APPLICATION_ID "com.example.flutter_quick_template") 11 | 12 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 13 | # versions of CMake. 14 | cmake_policy(SET CMP0063 NEW) 15 | 16 | # Load bundled libraries from the lib/ directory relative to the binary. 17 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 18 | 19 | # Root filesystem for cross-building. 20 | if(FLUTTER_TARGET_PLATFORM_SYSROOT) 21 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) 22 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) 23 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 24 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 25 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 26 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 27 | endif() 28 | 29 | # Define build configuration options. 30 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 31 | set(CMAKE_BUILD_TYPE "Debug" CACHE 32 | STRING "Flutter build mode" FORCE) 33 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 34 | "Debug" "Profile" "Release") 35 | endif() 36 | 37 | # Compilation settings that should be applied to most targets. 38 | # 39 | # Be cautious about adding new options here, as plugins use this function by 40 | # default. In most cases, you should add new options to specific targets instead 41 | # of modifying this function. 42 | function(APPLY_STANDARD_SETTINGS TARGET) 43 | target_compile_features(${TARGET} PUBLIC cxx_std_14) 44 | target_compile_options(${TARGET} PRIVATE -Wall -Werror) 45 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") 46 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") 47 | endfunction() 48 | 49 | # Flutter library and tool build rules. 50 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 51 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 52 | 53 | # System-level dependencies. 54 | find_package(PkgConfig REQUIRED) 55 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 56 | 57 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 58 | 59 | # Define the application target. To change its name, change BINARY_NAME above, 60 | # not the value here, or `flutter run` will no longer work. 61 | # 62 | # Any new source files that you add to the application should be added here. 63 | add_executable(${BINARY_NAME} 64 | "main.cc" 65 | "my_application.cc" 66 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 67 | ) 68 | 69 | # Apply the standard set of build settings. This can be removed for applications 70 | # that need different build settings. 71 | apply_standard_settings(${BINARY_NAME}) 72 | 73 | # Add dependency libraries. Add any application-specific dependencies here. 74 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 75 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 76 | 77 | # Run the Flutter tool portions of the build. This must not be removed. 78 | add_dependencies(${BINARY_NAME} flutter_assemble) 79 | 80 | # Only the install-generated bundle's copy of the executable will launch 81 | # correctly, since the resources must in the right relative locations. To avoid 82 | # people trying to run the unbundled copy, put it in a subdirectory instead of 83 | # the default top-level location. 84 | set_target_properties(${BINARY_NAME} 85 | PROPERTIES 86 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" 87 | ) 88 | 89 | 90 | # Generated plugin build rules, which manage building the plugins and adding 91 | # them to the application. 92 | include(flutter/generated_plugins.cmake) 93 | 94 | 95 | # === Installation === 96 | # By default, "installing" just makes a relocatable bundle in the build 97 | # directory. 98 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") 99 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 100 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 101 | endif() 102 | 103 | # Start with a clean build bundle directory every time. 104 | install(CODE " 105 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") 106 | " COMPONENT Runtime) 107 | 108 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 109 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") 110 | 111 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 112 | COMPONENT Runtime) 113 | 114 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 115 | COMPONENT Runtime) 116 | 117 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 118 | COMPONENT Runtime) 119 | 120 | foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) 121 | install(FILES "${bundled_library}" 122 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 123 | COMPONENT Runtime) 124 | endforeach(bundled_library) 125 | 126 | # Fully re-copy the assets directory on each build to avoid having stale files 127 | # from a previous install. 128 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 129 | install(CODE " 130 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 131 | " COMPONENT Runtime) 132 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 133 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 134 | 135 | # Install the AOT library on non-Debug builds only. 136 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") 137 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 138 | COMPONENT Runtime) 139 | endif() 140 | -------------------------------------------------------------------------------- /linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.10) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | 12 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 13 | # which isn't available in 3.10. 14 | function(list_prepend LIST_NAME PREFIX) 15 | set(NEW_LIST "") 16 | foreach(element ${${LIST_NAME}}) 17 | list(APPEND NEW_LIST "${PREFIX}${element}") 18 | endforeach(element) 19 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 20 | endfunction() 21 | 22 | # === Flutter Library === 23 | # System-level dependencies. 24 | find_package(PkgConfig REQUIRED) 25 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 26 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 27 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 28 | 29 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 30 | 31 | # Published to parent scope for install step. 32 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 33 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 34 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 35 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 36 | 37 | list(APPEND FLUTTER_LIBRARY_HEADERS 38 | "fl_basic_message_channel.h" 39 | "fl_binary_codec.h" 40 | "fl_binary_messenger.h" 41 | "fl_dart_project.h" 42 | "fl_engine.h" 43 | "fl_json_message_codec.h" 44 | "fl_json_method_codec.h" 45 | "fl_message_codec.h" 46 | "fl_method_call.h" 47 | "fl_method_channel.h" 48 | "fl_method_codec.h" 49 | "fl_method_response.h" 50 | "fl_plugin_registrar.h" 51 | "fl_plugin_registry.h" 52 | "fl_standard_message_codec.h" 53 | "fl_standard_method_codec.h" 54 | "fl_string_codec.h" 55 | "fl_value.h" 56 | "fl_view.h" 57 | "flutter_linux.h" 58 | ) 59 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 60 | add_library(flutter INTERFACE) 61 | target_include_directories(flutter INTERFACE 62 | "${EPHEMERAL_DIR}" 63 | ) 64 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 65 | target_link_libraries(flutter INTERFACE 66 | PkgConfig::GTK 67 | PkgConfig::GLIB 68 | PkgConfig::GIO 69 | ) 70 | add_dependencies(flutter flutter_assemble) 71 | 72 | # === Flutter tool backend === 73 | # _phony_ is a non-existent file to force this command to run every time, 74 | # since currently there's no way to get a full input/output list from the 75 | # flutter tool. 76 | add_custom_command( 77 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 78 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 79 | COMMAND ${CMAKE_COMMAND} -E env 80 | ${FLUTTER_TOOL_ENVIRONMENT} 81 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 82 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 83 | VERBATIM 84 | ) 85 | add_custom_target(flutter_assemble DEPENDS 86 | "${FLUTTER_LIBRARY}" 87 | ${FLUTTER_LIBRARY_HEADERS} 88 | ) 89 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | 10 | void fl_register_plugins(FlPluginRegistry* registry) { 11 | } 12 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | ) 7 | 8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 9 | ) 10 | 11 | set(PLUGIN_BUNDLED_LIBRARIES) 12 | 13 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 14 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 15 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 18 | endforeach(plugin) 19 | 20 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 23 | endforeach(ffi_plugin) 24 | -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | 10 | struct _MyApplication { 11 | GtkApplication parent_instance; 12 | char** dart_entrypoint_arguments; 13 | }; 14 | 15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 16 | 17 | // Implements GApplication::activate. 18 | static void my_application_activate(GApplication* application) { 19 | MyApplication* self = MY_APPLICATION(application); 20 | GtkWindow* window = 21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 22 | 23 | // Use a header bar when running in GNOME as this is the common style used 24 | // by applications and is the setup most users will be using (e.g. Ubuntu 25 | // desktop). 26 | // If running on X and not using GNOME then just use a traditional title bar 27 | // in case the window manager does more exotic layout, e.g. tiling. 28 | // If running on Wayland assume the header bar will work (may need changing 29 | // if future cases occur). 30 | gboolean use_header_bar = TRUE; 31 | #ifdef GDK_WINDOWING_X11 32 | GdkScreen* screen = gtk_window_get_screen(window); 33 | if (GDK_IS_X11_SCREEN(screen)) { 34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 36 | use_header_bar = FALSE; 37 | } 38 | } 39 | #endif 40 | if (use_header_bar) { 41 | GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 42 | gtk_widget_show(GTK_WIDGET(header_bar)); 43 | gtk_header_bar_set_title(header_bar, "flutter_quick_template"); 44 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 46 | } else { 47 | gtk_window_set_title(window, "flutter_quick_template"); 48 | } 49 | 50 | gtk_window_set_default_size(window, 1280, 720); 51 | gtk_widget_show(GTK_WIDGET(window)); 52 | 53 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 54 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 55 | 56 | FlView* view = fl_view_new(project); 57 | gtk_widget_show(GTK_WIDGET(view)); 58 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 59 | 60 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 61 | 62 | gtk_widget_grab_focus(GTK_WIDGET(view)); 63 | } 64 | 65 | // Implements GApplication::local_command_line. 66 | static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { 67 | MyApplication* self = MY_APPLICATION(application); 68 | // Strip out the first argument as it is the binary name. 69 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 70 | 71 | g_autoptr(GError) error = nullptr; 72 | if (!g_application_register(application, nullptr, &error)) { 73 | g_warning("Failed to register: %s", error->message); 74 | *exit_status = 1; 75 | return TRUE; 76 | } 77 | 78 | g_application_activate(application); 79 | *exit_status = 0; 80 | 81 | return TRUE; 82 | } 83 | 84 | // Implements GObject::dispose. 85 | static void my_application_dispose(GObject* object) { 86 | MyApplication* self = MY_APPLICATION(object); 87 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 88 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 89 | } 90 | 91 | static void my_application_class_init(MyApplicationClass* klass) { 92 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 93 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 94 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 95 | } 96 | 97 | static void my_application_init(MyApplication* self) {} 98 | 99 | MyApplication* my_application_new() { 100 | return MY_APPLICATION(g_object_new(my_application_get_type(), 101 | "application-id", APPLICATION_ID, 102 | "flags", G_APPLICATION_NON_UNIQUE, 103 | nullptr)); 104 | } 105 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import path_provider_foundation 9 | import shared_preferences_foundation 10 | import sqflite_darwin 11 | 12 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 13 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 14 | SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) 15 | SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) 16 | } 17 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /macos/Runner/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = flutter_quick_template 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterTestCode 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import Cocoa 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_quick_template 2 | description: This is a Flutter basic development framework built on third-party open-source libraries, providing convenient and easy-to-use utility classes and methods for quickly setting up a new Flutter project. 3 | # The following line prevents the package from being accidentally published to 4 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 5 | publish_to: "none" # Remove this line if you wish to publish to pub.dev 6 | 7 | # The following defines the version and build number for your application. 8 | # A version number is three numbers separated by dots, like 1.2.43 9 | # followed by an optional build number separated by a +. 10 | # Both the version and the builder number may be overridden in flutter 11 | # build by specifying --build-name and --build-number, respectively. 12 | # In Android, build-name is used as versionName while build-number used as versionCode. 13 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 14 | # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. 15 | # Read more about iOS versioning at 16 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 17 | # In Windows, build-name is used as the major, minor, and patch parts 18 | # of the product and file versions while build-number is used as the build suffix. 19 | version: 1.0.0+1 20 | 21 | environment: 22 | sdk: ">=3.1.0 <4.0.0" 23 | 24 | # Dependencies specify other packages that your package needs in order to work. 25 | # To automatically upgrade your package dependencies to the latest versions 26 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 27 | # dependencies can be manually updated by changing the version numbers below to 28 | # the latest version available on pub.dev. To see which dependencies have newer 29 | # versions available, run `flutter pub outdated`. 30 | dependencies: 31 | flutter: 32 | sdk: flutter 33 | get: 34 | flutter_screenutil: 35 | flutter_localizations: 36 | sdk: flutter 37 | lifecycle: 38 | shared_preferences: 39 | dio: 40 | logger: 41 | retrofit: '>=4.0.0 <5.0.0' 42 | json_annotation: ^4.9.0 43 | cached_network_image: 44 | flutter_smart_dialog: 45 | styled_widget: 46 | pretty_dio_logger: 47 | getx_route_annotations: 48 | mmkv: 49 | 50 | 51 | dev_dependencies: 52 | flutter_test: 53 | sdk: flutter 54 | 55 | # The "flutter_lints" package below contains a set of recommended lints to 56 | # encourage good coding practices. The lint set provided by the package is 57 | # activated in the `analysis_options.yaml` file located at the root of your 58 | # package. See that file for information about deactivating specific lint 59 | # rules and activating additional ones. 60 | flutter_lints: ^2.0.0 61 | retrofit_generator: '>=8.0.0 <10.0.0' 62 | build_runner: '>=2.3.0 <4.0.0' 63 | json_serializable: ^6.9.0 64 | getx_route_generator: 65 | # path: ./getx_route_generator/getx_route_generator 66 | 67 | 68 | # For information on the generic Dart part of this file, see the 69 | # following page: https://dart.dev/tools/pub/pubspec 70 | 71 | # The following section is specific to Flutter packages. 72 | flutter: 73 | # The following line ensures that the Material Icons font is 74 | # included with your application, so that you can use the icons in 75 | # the material Icons class. 76 | uses-material-design: true 77 | 78 | # To add assets to your application, add an assets section, like this: 79 | # An image asset can refer to one or more resolution-specific "variants", see 80 | # https://flutter.dev/assets-and-images/#resolution-aware 81 | 82 | # For details regarding adding assets from package dependencies, see 83 | # https://flutter.dev/assets-and-images/#from-packages 84 | 85 | # To add custom fonts to your application, add a fonts section here, 86 | # in this "flutter" section. Each entry in this list should have a 87 | # "family" key with the font family name, and a "fonts" key with a 88 | # list giving the asset and other descriptors for the font. For 89 | # example: 90 | # fonts: 91 | # - family: Schyler 92 | # fonts: 93 | # - asset: fonts/Schyler-Regular.ttf 94 | # - asset: fonts/Schyler-Italic.ttf 95 | # style: italic 96 | # - family: Trajan Pro 97 | # fonts: 98 | # - asset: fonts/TrajanPro.ttf 99 | # - asset: fonts/TrajanPro_Bold.ttf 100 | # weight: 700 101 | # 102 | # For details regarding fonts from package dependencies, 103 | # see https://flutter.dev/custom-fonts/#from-packages 104 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | 9 | void main() { 10 | } 11 | -------------------------------------------------------------------------------- /translations/en_US.json: -------------------------------------------------------------------------------- 1 | { 2 | "network":{ 3 | "network_error": "Network abnormality, please check the network connection status.", 4 | "system_error": "The system is busy, please try again later!", 5 | "net_disconnect": "Network not connected, please check and try again.", 6 | "request_cancel":"request canceled" 7 | } 8 | } -------------------------------------------------------------------------------- /translations/zh_CN.json: -------------------------------------------------------------------------------- 1 | { 2 | "network":{ 3 | "network_error": "网络异常,请检网络连接状态", 4 | "system_error": "系统繁忙,请稍后再试!", 5 | "net_disconnect": "网络未连接,请检查后重试", 6 | "request_cancel":"请求取消" 7 | } 8 | } -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | flutter_quick_template 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutter_quick_template", 3 | "short_name": "flutter_quick_template", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.14) 3 | project(flutter_quick_template LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "flutter_quick_template") 8 | 9 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 10 | # versions of CMake. 11 | cmake_policy(VERSION 3.14...3.25) 12 | 13 | # Define build configuration option. 14 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 15 | if(IS_MULTICONFIG) 16 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 17 | CACHE STRING "" FORCE) 18 | else() 19 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 20 | set(CMAKE_BUILD_TYPE "Debug" CACHE 21 | STRING "Flutter build mode" FORCE) 22 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 23 | "Debug" "Profile" "Release") 24 | endif() 25 | endif() 26 | # Define settings for the Profile build mode. 27 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 28 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 29 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 30 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 31 | 32 | # Use Unicode for all projects. 33 | add_definitions(-DUNICODE -D_UNICODE) 34 | 35 | # Compilation settings that should be applied to most targets. 36 | # 37 | # Be cautious about adding new options here, as plugins use this function by 38 | # default. In most cases, you should add new options to specific targets instead 39 | # of modifying this function. 40 | function(APPLY_STANDARD_SETTINGS TARGET) 41 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 42 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 43 | target_compile_options(${TARGET} PRIVATE /EHsc) 44 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 45 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 46 | endfunction() 47 | 48 | # Flutter library and tool build rules. 49 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 50 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 51 | 52 | # Application build; see runner/CMakeLists.txt. 53 | add_subdirectory("runner") 54 | 55 | 56 | # Generated plugin build rules, which manage building the plugins and adding 57 | # them to the application. 58 | include(flutter/generated_plugins.cmake) 59 | 60 | 61 | # === Installation === 62 | # Support files are copied into place next to the executable, so that it can 63 | # run in place. This is done instead of making a separate bundle (as on Linux) 64 | # so that building and running from within Visual Studio will work. 65 | set(BUILD_BUNDLE_DIR "$") 66 | # Make the "install" step default, as it's required to run. 67 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 68 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 69 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 70 | endif() 71 | 72 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 73 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 74 | 75 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 76 | COMPONENT Runtime) 77 | 78 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 79 | COMPONENT Runtime) 80 | 81 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 82 | COMPONENT Runtime) 83 | 84 | if(PLUGIN_BUNDLED_LIBRARIES) 85 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 86 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 87 | COMPONENT Runtime) 88 | endif() 89 | 90 | # Fully re-copy the assets directory on each build to avoid having stale files 91 | # from a previous install. 92 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 93 | install(CODE " 94 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 95 | " COMPONENT Runtime) 96 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 97 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 98 | 99 | # Install the AOT library on non-Debug builds only. 100 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 101 | CONFIGURATIONS Profile;Release 102 | COMPONENT Runtime) 103 | -------------------------------------------------------------------------------- /windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file controls Flutter-level build steps. It should not be edited. 2 | cmake_minimum_required(VERSION 3.14) 3 | 4 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 5 | 6 | # Configuration provided via flutter tool. 7 | include(${EPHEMERAL_DIR}/generated_config.cmake) 8 | 9 | # TODO: Move the rest of this into files in ephemeral. See 10 | # https://github.com/flutter/flutter/issues/57146. 11 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 12 | 13 | # Set fallback configurations for older versions of the flutter tool. 14 | if (NOT DEFINED FLUTTER_TARGET_PLATFORM) 15 | set(FLUTTER_TARGET_PLATFORM "windows-x64") 16 | endif() 17 | 18 | # === Flutter Library === 19 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 20 | 21 | # Published to parent scope for install step. 22 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 23 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 24 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 25 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 26 | 27 | list(APPEND FLUTTER_LIBRARY_HEADERS 28 | "flutter_export.h" 29 | "flutter_windows.h" 30 | "flutter_messenger.h" 31 | "flutter_plugin_registrar.h" 32 | "flutter_texture_registrar.h" 33 | ) 34 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 35 | add_library(flutter INTERFACE) 36 | target_include_directories(flutter INTERFACE 37 | "${EPHEMERAL_DIR}" 38 | ) 39 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 40 | add_dependencies(flutter flutter_assemble) 41 | 42 | # === Wrapper === 43 | list(APPEND CPP_WRAPPER_SOURCES_CORE 44 | "core_implementations.cc" 45 | "standard_codec.cc" 46 | ) 47 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 48 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 49 | "plugin_registrar.cc" 50 | ) 51 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 52 | list(APPEND CPP_WRAPPER_SOURCES_APP 53 | "flutter_engine.cc" 54 | "flutter_view_controller.cc" 55 | ) 56 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 57 | 58 | # Wrapper sources needed for a plugin. 59 | add_library(flutter_wrapper_plugin STATIC 60 | ${CPP_WRAPPER_SOURCES_CORE} 61 | ${CPP_WRAPPER_SOURCES_PLUGIN} 62 | ) 63 | apply_standard_settings(flutter_wrapper_plugin) 64 | set_target_properties(flutter_wrapper_plugin PROPERTIES 65 | POSITION_INDEPENDENT_CODE ON) 66 | set_target_properties(flutter_wrapper_plugin PROPERTIES 67 | CXX_VISIBILITY_PRESET hidden) 68 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 69 | target_include_directories(flutter_wrapper_plugin PUBLIC 70 | "${WRAPPER_ROOT}/include" 71 | ) 72 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 73 | 74 | # Wrapper sources needed for the runner. 75 | add_library(flutter_wrapper_app STATIC 76 | ${CPP_WRAPPER_SOURCES_CORE} 77 | ${CPP_WRAPPER_SOURCES_APP} 78 | ) 79 | apply_standard_settings(flutter_wrapper_app) 80 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 81 | target_include_directories(flutter_wrapper_app PUBLIC 82 | "${WRAPPER_ROOT}/include" 83 | ) 84 | add_dependencies(flutter_wrapper_app flutter_assemble) 85 | 86 | # === Flutter tool backend === 87 | # _phony_ is a non-existent file to force this command to run every time, 88 | # since currently there's no way to get a full input/output list from the 89 | # flutter tool. 90 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 91 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 92 | add_custom_command( 93 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 94 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 95 | ${CPP_WRAPPER_SOURCES_APP} 96 | ${PHONY_OUTPUT} 97 | COMMAND ${CMAKE_COMMAND} -E env 98 | ${FLUTTER_TOOL_ENVIRONMENT} 99 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 100 | ${FLUTTER_TARGET_PLATFORM} $ 101 | VERBATIM 102 | ) 103 | add_custom_target(flutter_assemble DEPENDS 104 | "${FLUTTER_LIBRARY}" 105 | ${FLUTTER_LIBRARY_HEADERS} 106 | ${CPP_WRAPPER_SOURCES_CORE} 107 | ${CPP_WRAPPER_SOURCES_PLUGIN} 108 | ${CPP_WRAPPER_SOURCES_APP} 109 | ) 110 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | 10 | void RegisterPlugins(flutter::PluginRegistry* registry) { 11 | } 12 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | ) 7 | 8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 9 | ) 10 | 11 | set(PLUGIN_BUNDLED_LIBRARIES) 12 | 13 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 14 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 15 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 18 | endforeach(plugin) 19 | 20 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 23 | endforeach(ffi_plugin) 24 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) 64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0,0 67 | #endif 68 | 69 | #if defined(FLUTTER_VERSION) 70 | #define VERSION_AS_STRING FLUTTER_VERSION 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.example" "\0" 93 | VALUE "FileDescription", "flutter_quick_template" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "flutter_quick_template" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2023 com.example. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "flutter_quick_template.exe" "\0" 98 | VALUE "ProductName", "flutter_quick_template" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | // Flutter can complete the first frame before the "show window" callback is 35 | // registered. The following call ensures a frame is pending to ensure the 36 | // window is shown. It is a no-op if the first frame hasn't completed yet. 37 | flutter_controller_->ForceRedraw(); 38 | 39 | return true; 40 | } 41 | 42 | void FlutterWindow::OnDestroy() { 43 | if (flutter_controller_) { 44 | flutter_controller_ = nullptr; 45 | } 46 | 47 | Win32Window::OnDestroy(); 48 | } 49 | 50 | LRESULT 51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 52 | WPARAM const wparam, 53 | LPARAM const lparam) noexcept { 54 | // Give Flutter, including plugins, an opportunity to handle window messages. 55 | if (flutter_controller_) { 56 | std::optional result = 57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 58 | lparam); 59 | if (result) { 60 | return *result; 61 | } 62 | } 63 | 64 | switch (message) { 65 | case WM_FONTCHANGE: 66 | flutter_controller_->engine()->ReloadSystemFonts(); 67 | break; 68 | } 69 | 70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 71 | } 72 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"flutter_quick_template", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panyiho/flutter_quick_template/32c4714df9844bf2d1d5971037b1f6e6f2ab9c50/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length <= 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /windows/runner/win32_window.cpp: -------------------------------------------------------------------------------- 1 | #include "win32_window.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "resource.h" 7 | 8 | namespace { 9 | 10 | /// Window attribute that enables dark mode window decorations. 11 | /// 12 | /// Redefined in case the developer's machine has a Windows SDK older than 13 | /// version 10.0.22000.0. 14 | /// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute 15 | #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE 16 | #define DWMWA_USE_IMMERSIVE_DARK_MODE 20 17 | #endif 18 | 19 | constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; 20 | 21 | /// Registry key for app theme preference. 22 | /// 23 | /// A value of 0 indicates apps should use dark mode. A non-zero or missing 24 | /// value indicates apps should use light mode. 25 | constexpr const wchar_t kGetPreferredBrightnessRegKey[] = 26 | L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; 27 | constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; 28 | 29 | // The number of Win32Window objects that currently exist. 30 | static int g_active_window_count = 0; 31 | 32 | using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); 33 | 34 | // Scale helper to convert logical scaler values to physical using passed in 35 | // scale factor 36 | int Scale(int source, double scale_factor) { 37 | return static_cast(source * scale_factor); 38 | } 39 | 40 | // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. 41 | // This API is only needed for PerMonitor V1 awareness mode. 42 | void EnableFullDpiSupportIfAvailable(HWND hwnd) { 43 | HMODULE user32_module = LoadLibraryA("User32.dll"); 44 | if (!user32_module) { 45 | return; 46 | } 47 | auto enable_non_client_dpi_scaling = 48 | reinterpret_cast( 49 | GetProcAddress(user32_module, "EnableNonClientDpiScaling")); 50 | if (enable_non_client_dpi_scaling != nullptr) { 51 | enable_non_client_dpi_scaling(hwnd); 52 | } 53 | FreeLibrary(user32_module); 54 | } 55 | 56 | } // namespace 57 | 58 | // Manages the Win32Window's window class registration. 59 | class WindowClassRegistrar { 60 | public: 61 | ~WindowClassRegistrar() = default; 62 | 63 | // Returns the singleton registrar instance. 64 | static WindowClassRegistrar* GetInstance() { 65 | if (!instance_) { 66 | instance_ = new WindowClassRegistrar(); 67 | } 68 | return instance_; 69 | } 70 | 71 | // Returns the name of the window class, registering the class if it hasn't 72 | // previously been registered. 73 | const wchar_t* GetWindowClass(); 74 | 75 | // Unregisters the window class. Should only be called if there are no 76 | // instances of the window. 77 | void UnregisterWindowClass(); 78 | 79 | private: 80 | WindowClassRegistrar() = default; 81 | 82 | static WindowClassRegistrar* instance_; 83 | 84 | bool class_registered_ = false; 85 | }; 86 | 87 | WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; 88 | 89 | const wchar_t* WindowClassRegistrar::GetWindowClass() { 90 | if (!class_registered_) { 91 | WNDCLASS window_class{}; 92 | window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); 93 | window_class.lpszClassName = kWindowClassName; 94 | window_class.style = CS_HREDRAW | CS_VREDRAW; 95 | window_class.cbClsExtra = 0; 96 | window_class.cbWndExtra = 0; 97 | window_class.hInstance = GetModuleHandle(nullptr); 98 | window_class.hIcon = 99 | LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); 100 | window_class.hbrBackground = 0; 101 | window_class.lpszMenuName = nullptr; 102 | window_class.lpfnWndProc = Win32Window::WndProc; 103 | RegisterClass(&window_class); 104 | class_registered_ = true; 105 | } 106 | return kWindowClassName; 107 | } 108 | 109 | void WindowClassRegistrar::UnregisterWindowClass() { 110 | UnregisterClass(kWindowClassName, nullptr); 111 | class_registered_ = false; 112 | } 113 | 114 | Win32Window::Win32Window() { 115 | ++g_active_window_count; 116 | } 117 | 118 | Win32Window::~Win32Window() { 119 | --g_active_window_count; 120 | Destroy(); 121 | } 122 | 123 | bool Win32Window::Create(const std::wstring& title, 124 | const Point& origin, 125 | const Size& size) { 126 | Destroy(); 127 | 128 | const wchar_t* window_class = 129 | WindowClassRegistrar::GetInstance()->GetWindowClass(); 130 | 131 | const POINT target_point = {static_cast(origin.x), 132 | static_cast(origin.y)}; 133 | HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); 134 | UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); 135 | double scale_factor = dpi / 96.0; 136 | 137 | HWND window = CreateWindow( 138 | window_class, title.c_str(), WS_OVERLAPPEDWINDOW, 139 | Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), 140 | Scale(size.width, scale_factor), Scale(size.height, scale_factor), 141 | nullptr, nullptr, GetModuleHandle(nullptr), this); 142 | 143 | if (!window) { 144 | return false; 145 | } 146 | 147 | UpdateTheme(window); 148 | 149 | return OnCreate(); 150 | } 151 | 152 | bool Win32Window::Show() { 153 | return ShowWindow(window_handle_, SW_SHOWNORMAL); 154 | } 155 | 156 | // static 157 | LRESULT CALLBACK Win32Window::WndProc(HWND const window, 158 | UINT const message, 159 | WPARAM const wparam, 160 | LPARAM const lparam) noexcept { 161 | if (message == WM_NCCREATE) { 162 | auto window_struct = reinterpret_cast(lparam); 163 | SetWindowLongPtr(window, GWLP_USERDATA, 164 | reinterpret_cast(window_struct->lpCreateParams)); 165 | 166 | auto that = static_cast(window_struct->lpCreateParams); 167 | EnableFullDpiSupportIfAvailable(window); 168 | that->window_handle_ = window; 169 | } else if (Win32Window* that = GetThisFromHandle(window)) { 170 | return that->MessageHandler(window, message, wparam, lparam); 171 | } 172 | 173 | return DefWindowProc(window, message, wparam, lparam); 174 | } 175 | 176 | LRESULT 177 | Win32Window::MessageHandler(HWND hwnd, 178 | UINT const message, 179 | WPARAM const wparam, 180 | LPARAM const lparam) noexcept { 181 | switch (message) { 182 | case WM_DESTROY: 183 | window_handle_ = nullptr; 184 | Destroy(); 185 | if (quit_on_close_) { 186 | PostQuitMessage(0); 187 | } 188 | return 0; 189 | 190 | case WM_DPICHANGED: { 191 | auto newRectSize = reinterpret_cast(lparam); 192 | LONG newWidth = newRectSize->right - newRectSize->left; 193 | LONG newHeight = newRectSize->bottom - newRectSize->top; 194 | 195 | SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, 196 | newHeight, SWP_NOZORDER | SWP_NOACTIVATE); 197 | 198 | return 0; 199 | } 200 | case WM_SIZE: { 201 | RECT rect = GetClientArea(); 202 | if (child_content_ != nullptr) { 203 | // Size and position the child window. 204 | MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, 205 | rect.bottom - rect.top, TRUE); 206 | } 207 | return 0; 208 | } 209 | 210 | case WM_ACTIVATE: 211 | if (child_content_ != nullptr) { 212 | SetFocus(child_content_); 213 | } 214 | return 0; 215 | 216 | case WM_DWMCOLORIZATIONCOLORCHANGED: 217 | UpdateTheme(hwnd); 218 | return 0; 219 | } 220 | 221 | return DefWindowProc(window_handle_, message, wparam, lparam); 222 | } 223 | 224 | void Win32Window::Destroy() { 225 | OnDestroy(); 226 | 227 | if (window_handle_) { 228 | DestroyWindow(window_handle_); 229 | window_handle_ = nullptr; 230 | } 231 | if (g_active_window_count == 0) { 232 | WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); 233 | } 234 | } 235 | 236 | Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { 237 | return reinterpret_cast( 238 | GetWindowLongPtr(window, GWLP_USERDATA)); 239 | } 240 | 241 | void Win32Window::SetChildContent(HWND content) { 242 | child_content_ = content; 243 | SetParent(content, window_handle_); 244 | RECT frame = GetClientArea(); 245 | 246 | MoveWindow(content, frame.left, frame.top, frame.right - frame.left, 247 | frame.bottom - frame.top, true); 248 | 249 | SetFocus(child_content_); 250 | } 251 | 252 | RECT Win32Window::GetClientArea() { 253 | RECT frame; 254 | GetClientRect(window_handle_, &frame); 255 | return frame; 256 | } 257 | 258 | HWND Win32Window::GetHandle() { 259 | return window_handle_; 260 | } 261 | 262 | void Win32Window::SetQuitOnClose(bool quit_on_close) { 263 | quit_on_close_ = quit_on_close; 264 | } 265 | 266 | bool Win32Window::OnCreate() { 267 | // No-op; provided for subclasses. 268 | return true; 269 | } 270 | 271 | void Win32Window::OnDestroy() { 272 | // No-op; provided for subclasses. 273 | } 274 | 275 | void Win32Window::UpdateTheme(HWND const window) { 276 | DWORD light_mode; 277 | DWORD light_mode_size = sizeof(light_mode); 278 | LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, 279 | kGetPreferredBrightnessRegValue, 280 | RRF_RT_REG_DWORD, nullptr, &light_mode, 281 | &light_mode_size); 282 | 283 | if (result == ERROR_SUCCESS) { 284 | BOOL enable_dark_mode = light_mode == 0; 285 | DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, 286 | &enable_dark_mode, sizeof(enable_dark_mode)); 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates a win32 window with |title| that is positioned and sized using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size this function will scale the inputted width and height as 35 | // as appropriate for the default monitor. The window is invisible until 36 | // |Show| is called. Returns true if the window was created successfully. 37 | bool Create(const std::wstring& title, const Point& origin, const Size& size); 38 | 39 | // Show the current window. Returns true if the window was successfully shown. 40 | bool Show(); 41 | 42 | // Release OS resources associated with window. 43 | void Destroy(); 44 | 45 | // Inserts |content| into the window tree. 46 | void SetChildContent(HWND content); 47 | 48 | // Returns the backing Window handle to enable clients to set icon and other 49 | // window properties. Returns nullptr if the window has been destroyed. 50 | HWND GetHandle(); 51 | 52 | // If true, closing this window will quit the application. 53 | void SetQuitOnClose(bool quit_on_close); 54 | 55 | // Return a RECT representing the bounds of the current client area. 56 | RECT GetClientArea(); 57 | 58 | protected: 59 | // Processes and route salient window messages for mouse handling, 60 | // size change and DPI. Delegates handling of these to member overloads that 61 | // inheriting classes can handle. 62 | virtual LRESULT MessageHandler(HWND window, 63 | UINT const message, 64 | WPARAM const wparam, 65 | LPARAM const lparam) noexcept; 66 | 67 | // Called when CreateAndShow is called, allowing subclass window-related 68 | // setup. Subclasses should return false if setup fails. 69 | virtual bool OnCreate(); 70 | 71 | // Called when Destroy is called. 72 | virtual void OnDestroy(); 73 | 74 | private: 75 | friend class WindowClassRegistrar; 76 | 77 | // OS callback called by message pump. Handles the WM_NCCREATE message which 78 | // is passed when the non-client area is being created and enables automatic 79 | // non-client DPI scaling so that the non-client area automatically 80 | // responds to changes in DPI. All other messages are handled by 81 | // MessageHandler. 82 | static LRESULT CALLBACK WndProc(HWND const window, 83 | UINT const message, 84 | WPARAM const wparam, 85 | LPARAM const lparam) noexcept; 86 | 87 | // Retrieves a class instance pointer for |window| 88 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 89 | 90 | // Update the window frame's theme to match the system theme. 91 | static void UpdateTheme(HWND const window); 92 | 93 | bool quit_on_close_ = false; 94 | 95 | // window handle for top level window. 96 | HWND window_handle_ = nullptr; 97 | 98 | // window handle for hosted content. 99 | HWND child_content_ = nullptr; 100 | }; 101 | 102 | #endif // RUNNER_WIN32_WINDOW_H_ 103 | --------------------------------------------------------------------------------