├── .gitignore
├── .metadata
├── README.md
├── alipwechat.png
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ └── src
│ │ ├── debug
│ │ ├── AndroidManifest.xml
│ │ └── lib
│ │ │ └── l10n
│ │ │ └── intl_zh.arb
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── lijiaqi
│ │ │ │ └── bedrock
│ │ │ │ ├── AppCrashHandler.java
│ │ │ │ ├── BaseApp.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── plugin
│ │ │ │ └── BedrockPlugin.java
│ │ │ │ ├── protect
│ │ │ │ ├── ActivityStackManager.java
│ │ │ │ ├── AndroidPlatformProtect.java
│ │ │ │ ├── IProtect.java
│ │ │ │ ├── handler
│ │ │ │ │ ├── ActivityExceptionHandler.java
│ │ │ │ │ └── DefaultActivityExceptionHandler.java
│ │ │ │ └── zone
│ │ │ │ │ ├── ActivityStartProtect.java
│ │ │ │ │ ├── ChildThreadProtect.java
│ │ │ │ │ └── UIThreadProtect.java
│ │ │ │ ├── test
│ │ │ │ ├── TestPage1.java
│ │ │ │ ├── TestPage2.java
│ │ │ │ └── readme
│ │ │ │ └── util
│ │ │ │ └── ReflexUtil.java
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── lijiaqi
│ │ │ │ └── flutter_bedrock
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ └── launch_background.xml
│ │ │ ├── layout
│ │ │ ├── activity_test_page1.xml
│ │ │ └── activity_test_page2.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
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ │ └── xml
│ │ │ └── provider_paths.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── settings_aar.gradle
├── assets
├── images
│ ├── bg.png
│ ├── car.png
│ ├── icons
│ │ ├── icon_back_black.png
│ │ └── suggestion_camera.png
│ └── sds.png
└── test_icon.png
├── build.yaml
├── ios
├── .gitignore
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── Podfile
├── Podfile.lock
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── 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
├── lib
├── base_framework
│ ├── config
│ │ ├── app_config.dart
│ │ ├── frame_constant.dart
│ │ ├── global_provider_manager.dart
│ │ ├── net
│ │ │ ├── base_http.dart
│ │ │ └── bedrock_http.dart
│ │ ├── router_manager.dart
│ │ └── storage_manager.dart
│ ├── constant.dart
│ ├── empty.dart
│ ├── exception
│ │ ├── base_exception.dart
│ │ ├── data_missing_exception.dart
│ │ ├── un_authorized_exception.dart
│ │ ├── un_handle_exception.dart
│ │ ├── user_unbind_exception.dart
│ │ └── verification_code_exception.dart
│ ├── extension
│ │ ├── list_extension.dart
│ │ └── size_adapter_extension.dart
│ ├── factory
│ │ ├── page
│ │ │ └── page_animation_builder.dart
│ │ └── useless.dart
│ ├── native
│ │ └── native_method_manager.dart
│ ├── observe
│ │ ├── app_status
│ │ │ ├── app_status_observe.dart
│ │ │ ├── net_observer.dart
│ │ │ └── server_notify_observer.dart
│ │ ├── own_navigator_observe.dart
│ │ └── route
│ │ │ └── router_binding.dart
│ ├── ui
│ │ ├── anim
│ │ │ ├── a.dart
│ │ │ └── page_route_anim
│ │ │ │ ├── fade_animation.dart
│ │ │ │ ├── no_animation.dart
│ │ │ │ ├── size_scale_animation.dart
│ │ │ │ └── slide_animation.dart
│ │ ├── behavior
│ │ │ └── over_scroll_behavior.dart
│ │ ├── empty.dart
│ │ └── widget
│ │ │ ├── activity_indicator.dart
│ │ │ ├── bottom_nav_bar_no_ink.dart
│ │ │ ├── circle_border_container.dart
│ │ │ ├── detail_image_widget.dart
│ │ │ ├── ffloat.dart
│ │ │ ├── float_container_widget.dart
│ │ │ ├── image
│ │ │ └── image_editor.dart
│ │ │ ├── no_ink_well_factory.dart
│ │ │ ├── notification
│ │ │ ├── common_notification_widget.dart
│ │ │ └── notification_handler.dart
│ │ │ ├── popup_window.dart
│ │ │ ├── progress_widget.dart
│ │ │ ├── provider_widget.dart
│ │ │ ├── route
│ │ │ ├── pop_window_builder.dart
│ │ │ └── route_aware_widget.dart
│ │ │ ├── skeleton
│ │ │ ├── base_skeleton
│ │ │ │ └── base_skeleton_widget.dart
│ │ │ └── skeleton_rect.dart
│ │ │ ├── view_state_widget.dart
│ │ │ └── web
│ │ │ ├── html_page.dart
│ │ │ └── web_page.dart
│ ├── utils
│ │ ├── colors_util.dart
│ │ ├── exception_pitcher.dart
│ │ ├── image_helper.dart
│ │ ├── isolate
│ │ │ ├── app_private_ioslate.dart
│ │ │ └── factory
│ │ │ │ ├── proxy_isolate.dart
│ │ │ │ ├── task_warpper.dart
│ │ │ │ ├── work_isolate_wrapper.dart
│ │ │ │ └── worker_isolate.dart
│ │ ├── little_util.dart
│ │ ├── mmkv_flutter.dart
│ │ ├── notification_util.dart
│ │ ├── platform_utils.dart
│ │ ├── refresh_helper.dart
│ │ ├── show_image_util.dart
│ │ └── string_helper.dart
│ ├── view_model
│ │ ├── app_model
│ │ │ ├── app_cache_model.dart
│ │ │ ├── app_status_model.dart
│ │ │ ├── device_model.dart
│ │ │ ├── locale_model.dart
│ │ │ └── user_view_model.dart
│ │ ├── custome_view_state_model.dart
│ │ ├── handle
│ │ │ └── exception_handler.dart
│ │ ├── interface
│ │ │ └── cache_data_factory.dart
│ │ ├── list_view_state_model.dart
│ │ ├── refresh_list_view_state_model.dart
│ │ ├── single_view_state_model.dart
│ │ ├── view_state.dart
│ │ └── view_state_model.dart
│ └── widget_state
│ │ ├── base_state.dart
│ │ ├── base_stateless_widget.dart
│ │ ├── binding
│ │ └── manipulate_widget_binding.dart
│ │ ├── page_state.dart
│ │ └── widget_state.dart
├── generated
│ ├── intl
│ │ ├── messages_all.dart
│ │ ├── messages_en.dart
│ │ └── messages_zh.dart
│ └── l10n.dart
├── l10n
│ ├── intl_en.arb
│ └── intl_zh.arb
├── main.dart
├── main.reflectable.dart
├── page
│ ├── a.dart
│ ├── demo_page
│ │ ├── demo_page.dart
│ │ ├── exception
│ │ │ ├── exception_main_page.dart
│ │ │ ├── exception_notify_page.dart
│ │ │ ├── exception_view_model.dart
│ │ │ ├── handle_exception_page.dart
│ │ │ └── unknow_page.dart
│ │ ├── image
│ │ │ ├── image_main_demo.dart
│ │ │ ├── nine_image
│ │ │ │ ├── nine_flow_delegate.dart
│ │ │ │ ├── nine_image_state.dart
│ │ │ │ └── nine_image_vm.dart
│ │ │ ├── nine_image_page.dart
│ │ │ └── pick_image_page.dart
│ │ ├── isolate
│ │ │ └── isolate_page.dart
│ │ ├── local_i10l
│ │ │ └── local_page.dart
│ │ ├── main
│ │ │ ├── fake_constant.dart
│ │ │ ├── first
│ │ │ │ ├── cache_data_page.dart
│ │ │ │ ├── entity
│ │ │ │ │ ├── cache_entity.dart
│ │ │ │ │ ├── first_card_entity.dart
│ │ │ │ │ └── first_entity.dart
│ │ │ │ ├── ffloat_page.dart
│ │ │ │ ├── update_page.dart
│ │ │ │ ├── view_model
│ │ │ │ │ ├── cache_page_vm.dart
│ │ │ │ │ ├── first_view_model.dart
│ │ │ │ │ └── update_view_model.dart
│ │ │ │ └── widget
│ │ │ │ │ ├── first_banner.dart
│ │ │ │ │ └── first_skeleton_page.dart
│ │ │ ├── first_page.dart
│ │ │ ├── login
│ │ │ │ ├── login_page.dart
│ │ │ │ └── login_view_model.dart
│ │ │ ├── second
│ │ │ │ ├── entity
│ │ │ │ │ └── second_entity.dart
│ │ │ │ └── view_model
│ │ │ │ │ └── second_view_model.dart
│ │ │ ├── second_page.dart
│ │ │ └── third_page.dart
│ │ ├── main_page.dart
│ │ ├── notification
│ │ │ ├── notification_page.dart
│ │ │ └── notify_target_page.dart
│ │ ├── other
│ │ │ ├── cross_list
│ │ │ │ ├── body_item_widget.dart
│ │ │ │ ├── cross_list_page.dart
│ │ │ │ ├── cross_list_vm.dart
│ │ │ │ └── tab_item_widget.dart
│ │ │ ├── custom_dialog_page.dart
│ │ │ ├── eye_3d.dart
│ │ │ ├── float_layer_widget
│ │ │ │ └── shake_float_widget.dart
│ │ │ ├── little_util_page.dart
│ │ │ ├── part_refresh_page.dart
│ │ │ ├── request_permission_page.dart
│ │ │ ├── scroll_page.dart
│ │ │ ├── selector_demo_page.dart
│ │ │ ├── simple_list
│ │ │ │ └── simple_list_page.dart
│ │ │ ├── test_android_page.dart
│ │ │ └── timer_page.dart
│ │ ├── other_demo_page.dart
│ │ ├── route_anim
│ │ │ ├── fade_page.dart
│ │ │ ├── route_animation_page.dart
│ │ │ ├── scale_page.dart
│ │ │ └── slide_page.dart
│ │ ├── slide_out_page.dart
│ │ └── start
│ │ │ ├── pages
│ │ │ ├── page_1.dart
│ │ │ ├── page_2.dart
│ │ │ └── page_3.dart
│ │ │ ├── start_page.dart
│ │ │ └── with_value_page.dart
│ ├── demo_widget
│ │ └── custom_drawer.dart
│ ├── exception
│ │ └── exception_page.dart
│ └── mine
│ │ └── entity
│ │ └── user_entity.dart
└── service_api
│ ├── bedrock_repository_proxy.dart
│ └── section_one_api.dart
├── pubspec.lock
├── pubspec.yaml
├── test
└── widget_test.dart
└── update_log.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .flutter-plugins-dependencies
28 | .packages
29 | .pub-cache/
30 | .pub/
31 | /build/
32 |
33 | # Web related
34 | lib/generated_plugin_registrant.dart
35 |
36 | # Exceptions to above rules.
37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
38 |
--------------------------------------------------------------------------------
/.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: 27321ebbad34b0a3fafe99fac037102196d655ff
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/alipwechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/alipwechat.png
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion 31
30 | buildToolsVersion '30.0.2'
31 |
32 | sourceSets {
33 | main.java.srcDirs += 'src/main/kotlin'
34 | }
35 |
36 | lintOptions {
37 | disable 'InvalidPackage'
38 | }
39 |
40 | defaultConfig {
41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
42 | applicationId "com.lijiaqi.flutter_bedrock"
43 | minSdkVersion 21
44 | targetSdkVersion 31
45 | versionCode flutterVersionCode.toInteger()
46 | versionName flutterVersionName
47 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
48 | }
49 |
50 | buildTypes {
51 | release {
52 | // TODO: Add your own signing config for the release build.
53 | // Signing with the debug keys for now, so `flutter run --release` works.
54 | signingConfig signingConfigs.debug
55 | }
56 | }
57 |
58 | }
59 |
60 | flutter {
61 | source '../..'
62 | }
63 |
64 | dependencies {
65 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
66 | implementation 'androidx.appcompat:appcompat:1.2.0'
67 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
68 | testImplementation 'junit:junit:4.12'
69 | androidTestImplementation 'androidx.test:runner:1.1.1'
70 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
71 | }
72 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/debug/lib/l10n/intl_zh.arb:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/android/app/src/main/java/com/lijiaqi/bedrock/AppCrashHandler.java:
--------------------------------------------------------------------------------
1 | package com.lijiaqi.bedrock;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 |
6 | /**
7 | * @author LiJiaqi
8 | * @date 2020/12/12
9 | * Description:
10 | * handle the thread exception,and avoid app crash.
11 | * in fact,this Singleton doesn't make much sense...but,easy to use.
12 | */
13 | class AppCrashHandler implements Thread.UncaughtExceptionHandler {
14 |
15 | static final String TAG = "Bedrock_Crash_Handler:";
16 | final String slash = "------------------";
17 |
18 | static private volatile AppCrashHandler singleton;
19 |
20 | public static AppCrashHandler getInstance(Context context){
21 | if(singleton == null){
22 | synchronized (AppCrashHandler.class){
23 | if(singleton == null){
24 | singleton = new AppCrashHandler(context);
25 | }
26 | }
27 | }
28 | return singleton;
29 | }
30 | private Context context;
31 | AppCrashHandler(Context context){
32 | this.context = context;
33 | }
34 |
35 | ///处理子线程异常
36 | public void initChildThreadProtect(){
37 | Thread.setDefaultUncaughtExceptionHandler(this);
38 | }
39 | ///处理子线程异常
40 | @Override
41 | public void uncaughtException(Thread t, Throwable e) {
42 | Log.d(TAG, slash+TAG+slash);
43 | Log.d(TAG,t.getName());
44 | e.printStackTrace();
45 | Log.d(TAG, slash+TAG+slash);
46 |
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/lijiaqi/bedrock/BaseApp.java:
--------------------------------------------------------------------------------
1 | package com.lijiaqi.bedrock;
2 |
3 | import com.lijiaqi.bedrock.protect.AndroidPlatformProtect;
4 | import com.lijiaqi.bedrock.protect.handler.DefaultActivityExceptionHandler;
5 |
6 | import io.flutter.app.FlutterApplication;
7 |
8 | /**
9 | * @author LiJiaqi
10 | * @date 2020/12/12
11 | * Description:
12 | *
13 | * Tip 1:
14 | * 如果你有闪屏页优化需求,可以参考这篇文章,也许对你有所帮助。
15 | * https://juejin.cn/post/6913360134429376525
16 | */
17 | public class BaseApp extends FlutterApplication {
18 | @Override
19 | public void onCreate() {
20 | super.onCreate();
21 |
22 | // 如果是纯flutter项目,
23 | // 可以考虑注释掉这两个保护[protectUIThread]和[protectActivityStart]
24 | // 混合项目的话,可以考虑开启
25 | AndroidPlatformProtect.initProtect(new DefaultActivityExceptionHandler())
26 | ///处理UI线程的异常
27 | //.protectUIThread()
28 | ///处理 activity生命周期的异常
29 | //注释原因: 功能,使用了反射。
30 | // 在混淆下可能会无效或者出现无法预知的问题,建议开启后反复测试
31 | //.protectActivityStart()
32 | ///处理子线程异常
33 | .protectChildThread()
34 | .init(this);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/lijiaqi/bedrock/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.lijiaqi.bedrock;
2 |
3 |
4 | /*
5 | * Author : LiJiqqi
6 | * Date : 2020/7/6
7 | */
8 |
9 | import androidx.annotation.NonNull;
10 |
11 | import com.lijiaqi.bedrock.plugin.BedrockPlugin;
12 |
13 | import io.flutter.embedding.android.FlutterActivity;
14 | import io.flutter.embedding.engine.FlutterEngine;
15 | import io.flutter.plugins.GeneratedPluginRegistrant;
16 |
17 | public class MainActivity extends FlutterActivity {
18 | @Override
19 | public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
20 | GeneratedPluginRegistrant.registerWith(flutterEngine);
21 | flutterEngine.getPlugins().add(new BedrockPlugin(this));
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/lijiaqi/bedrock/protect/ActivityStackManager.java:
--------------------------------------------------------------------------------
1 | package com.lijiaqi.bedrock.protect;
2 |
3 | import android.app.Activity;
4 | import android.app.Application;
5 | import android.os.Bundle;
6 |
7 | import androidx.annotation.NonNull;
8 | import androidx.annotation.Nullable;
9 |
10 | import java.util.LinkedList;
11 |
12 | /**
13 | * @author LiJiaqi
14 | * @date 2020/12/13
15 | * Description:
16 | */
17 | public class ActivityStackManager{
18 |
19 | private static volatile ActivityStackManager singleton;
20 | public static ActivityStackManager getInstance(){
21 | if(singleton == null){
22 | synchronized (ActivityStackManager.class){
23 | if (singleton == null){
24 | singleton = new ActivityStackManager();
25 | }
26 | }
27 | }
28 | return singleton;
29 | }
30 |
31 | final private LinkedList activities = new LinkedList<>();
32 |
33 | public void init(Application app){
34 | app.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
35 | @Override
36 | public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle bundle) {
37 | activities.add(activity);
38 | }
39 |
40 | @Override
41 | public void onActivityStarted(@NonNull Activity activity) {
42 |
43 | }
44 |
45 | @Override
46 | public void onActivityResumed(@NonNull Activity activity) {
47 |
48 | }
49 |
50 | @Override
51 | public void onActivityPaused(@NonNull Activity activity) {
52 |
53 | }
54 |
55 | @Override
56 | public void onActivityStopped(@NonNull Activity activity) {
57 |
58 | }
59 |
60 | @Override
61 | public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle bundle) {
62 |
63 | }
64 |
65 | @Override
66 | public void onActivityDestroyed(@NonNull Activity activity) {
67 | activities.remove(activity);
68 |
69 | }
70 | });
71 | }
72 |
73 | public Activity exceptionBirthplaceActivity(Exception e){
74 | if(activities.isEmpty()) return null;
75 | String className = activities.getLast().getClass().getName();
76 | if(e.getStackTrace()[0].getClassName().contains(className)){
77 | return activities.getLast();
78 | }
79 | return null;
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/lijiaqi/bedrock/protect/AndroidPlatformProtect.java:
--------------------------------------------------------------------------------
1 | package com.lijiaqi.bedrock.protect;
2 |
3 | import android.app.Application;
4 |
5 | import com.lijiaqi.bedrock.protect.handler.ActivityExceptionHandler;
6 | import com.lijiaqi.bedrock.protect.zone.ActivityStartProtect;
7 | import com.lijiaqi.bedrock.protect.zone.ChildThreadProtect;
8 | import com.lijiaqi.bedrock.protect.zone.UIThreadProtect;
9 |
10 | import java.util.ArrayList;
11 | import java.util.List;
12 |
13 | /**
14 | * @author LiJiaqi
15 | * @date 2020/12/13
16 | * Description:
17 | * 确保android 平台的异常不会导致应用崩溃(除了系统异常,或连续性异常)
18 | */
19 |
20 |
21 |
22 | public class AndroidPlatformProtect {
23 |
24 | private final List protectStrategyList = new ArrayList<>();
25 |
26 | private static volatile AndroidPlatformProtect singleton;
27 |
28 | public static AndroidPlatformProtect initProtect(ActivityExceptionHandler exceptionHandler){
29 | if(singleton == null){
30 | synchronized (AndroidPlatformProtect.class){
31 | if(singleton == null){
32 | singleton = new AndroidPlatformProtect(exceptionHandler);
33 | }
34 | }
35 | }
36 | return singleton;
37 | }
38 | private AndroidPlatformProtect(ActivityExceptionHandler exceptionHandler){
39 | if(exceptionHandler == null) throw new RuntimeException("ActivityExceptionHandler is null," +
40 | " you must set a exceptionHandler, that subclass of the ActivityExceptionHandler.");
41 | this.exceptionHandler = exceptionHandler;
42 | }
43 |
44 | public static AndroidPlatformProtect getInstance(){
45 | if(singleton == null) throw new RuntimeException("you need call method 'initProtect' first than get a Instance.");
46 | return singleton;
47 | }
48 |
49 | ///activity 异常善后
50 | private final ActivityExceptionHandler exceptionHandler;
51 | public ActivityExceptionHandler getExceptionHandler(){
52 | return exceptionHandler;
53 | }
54 |
55 | public AndroidPlatformProtect protectActivityStart(){
56 | protectStrategyList.add(new ActivityStartProtect());
57 | return singleton;
58 | }
59 |
60 | public AndroidPlatformProtect protectUIThread(){
61 | protectStrategyList.add(new UIThreadProtect());
62 | return singleton;
63 | }
64 |
65 |
66 | public AndroidPlatformProtect protectChildThread(){
67 | protectStrategyList.add(new ChildThreadProtect());
68 | return singleton;
69 | }
70 |
71 |
72 | public void init(Application app){
73 | if(protectStrategyList.isEmpty()){
74 | throw new RuntimeException("You must add protect strategy by method \"protectXXX\" before \"init()\"");
75 | }
76 | for(IProtect protect : protectStrategyList){
77 | protect.protect(app);
78 | }
79 | }
80 |
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/lijiaqi/bedrock/protect/IProtect.java:
--------------------------------------------------------------------------------
1 | package com.lijiaqi.bedrock.protect;
2 |
3 | import android.app.Application;
4 |
5 | /**
6 | * @author LiJiaqi
7 | * @date 2020/12/13
8 | * Description:
9 | */
10 | public interface IProtect {
11 | void protect(Application application);
12 | }
13 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/lijiaqi/bedrock/protect/handler/ActivityExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package com.lijiaqi.bedrock.protect.handler;
2 |
3 |
4 | /*
5 | * Author : LiJiqqi
6 | * Date : 2020/12/16
7 | * [protectUIThread]和[protectActivityStart] 异常善后类
8 | */
9 |
10 | import android.app.Activity;
11 |
12 | public abstract class ActivityExceptionHandler {
13 |
14 | public abstract void onException(Activity activity,Exception e);
15 | public abstract void onChildThreadException(Thread t, Throwable e);
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/lijiaqi/bedrock/protect/handler/DefaultActivityExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package com.lijiaqi.bedrock.protect.handler;
2 |
3 |
4 | /*
5 | * Author : LiJiqqi
6 | * Date : 2020/12/16
7 | */
8 |
9 | import android.app.Activity;
10 | import android.util.Log;
11 |
12 |
13 |
14 | public class DefaultActivityExceptionHandler extends ActivityExceptionHandler {
15 | static private final String TAG = "Exception_Handler:";
16 | private final String slash = "------------------";
17 | ///
18 |
19 | /**
20 | *[protectUIThread]和[protectActivityStart]异常将会调用这个方法
21 | *
22 | * {@link com.lijiaqi.bedrock.protect.zone.ActivityStartProtect}
23 | * {@link com.lijiaqi.bedrock.protect.zone.UIThreadProtect}
24 | */
25 |
26 | @Override
27 | public void onException(Activity activity, Exception e) {
28 | Log.i(TAG,"==========Activity(UI thread) 发生了一个异常=========");
29 | if(e != null)Log.d(TAG,""+e.getMessage());
30 | if(activity != null) activity.finish();
31 | }
32 |
33 |
34 |
35 | /**
36 | *子线程异常会调用此方法 ,
37 | * 注意,此方法内的线程切换
38 | * {@link com.lijiaqi.bedrock.protect.zone.ChildThreadProtect}
39 | */
40 | @Override
41 | public void onChildThreadException(Thread t, Throwable e) {
42 | Log.d(TAG, slash+TAG+slash);
43 | Log.d(TAG,t.getName());
44 | e.printStackTrace();
45 | Log.d(TAG, slash+TAG+slash);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/lijiaqi/bedrock/protect/zone/ChildThreadProtect.java:
--------------------------------------------------------------------------------
1 | package com.lijiaqi.bedrock.protect.zone;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import android.util.Log;
6 |
7 | import com.lijiaqi.bedrock.protect.AndroidPlatformProtect;
8 | import com.lijiaqi.bedrock.protect.IProtect;
9 | import com.lijiaqi.bedrock.protect.handler.ActivityExceptionHandler;
10 |
11 |
12 | /**
13 | * @author LiJiaqi
14 | * @date 2020/12/13
15 | * Description:
16 | * 处理子线程触发的异常
17 | */
18 |
19 | public class ChildThreadProtect implements IProtect {
20 | @Override
21 | public void protect(Application application) {
22 | ChildThreadHandler.getInstance(application).initChildThreadProtect();
23 | }
24 | }
25 |
26 | /// handle the thread exception,and avoid app crash.
27 | /// in fact,this Singleton doesn't make much sense...but,easy to use.
28 |
29 | class ChildThreadHandler implements Thread.UncaughtExceptionHandler{
30 |
31 | static private volatile ChildThreadHandler singleton;
32 |
33 | public static ChildThreadHandler getInstance(Context context){
34 | if(singleton == null){
35 | synchronized (ChildThreadHandler.class){
36 | if(singleton == null){
37 | singleton = new ChildThreadHandler(context);
38 | }
39 | }
40 | }
41 | return singleton;
42 | }
43 | private final ActivityExceptionHandler exceptionHandler;
44 | private final Context context;
45 | ChildThreadHandler(Context context){
46 | this.context = context;
47 | this.exceptionHandler = AndroidPlatformProtect.getInstance().getExceptionHandler();
48 | if(exceptionHandler == null) throw new RuntimeException("ActivityExceptionHandler is null," +
49 | " you must set a exceptionHandler, that subclass of the ActivityExceptionHandler.");
50 | }
51 |
52 | ///处理子线程异常
53 | public void initChildThreadProtect(){
54 | Thread.setDefaultUncaughtExceptionHandler(this);
55 | }
56 | ///处理子线程异常
57 | @Override
58 | public void uncaughtException(Thread t, Throwable e) {
59 | exceptionHandler.onChildThreadException(t, e);
60 | }
61 | }
--------------------------------------------------------------------------------
/android/app/src/main/java/com/lijiaqi/bedrock/test/TestPage1.java:
--------------------------------------------------------------------------------
1 | package com.lijiaqi.bedrock.test;
2 |
3 | import androidx.appcompat.app.AppCompatActivity;
4 |
5 | import android.app.Activity;
6 | import android.content.Intent;
7 | import android.os.Bundle;
8 | import android.view.View;
9 |
10 | import com.lijiaqi.flutter_bedrock.R;
11 |
12 | public class TestPage1 extends Activity {
13 |
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | setContentView(R.layout.activity_test_page1);
18 | findViewById(R.id.btn_1).setOnClickListener(new View.OnClickListener() {
19 | @Override
20 | public void onClick(View v) {
21 | throw new RuntimeException("点击事件触发了一个UI异常");
22 | }
23 | });
24 | findViewById(R.id.btn_2).setOnClickListener(new View.OnClickListener() {
25 | @Override
26 | public void onClick(View v) {
27 | startActivity(new Intent(TestPage1.this,TestPage2.class));
28 | }
29 | });
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/lijiaqi/bedrock/test/TestPage2.java:
--------------------------------------------------------------------------------
1 | package com.lijiaqi.bedrock.test;
2 |
3 | import androidx.appcompat.app.AppCompatActivity;
4 |
5 | import android.app.Activity;
6 | import android.os.Bundle;
7 |
8 | import com.lijiaqi.flutter_bedrock.R;
9 |
10 | public class TestPage2 extends Activity {
11 |
12 | @Override
13 | protected void onCreate(Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 | setContentView(R.layout.activity_test_page2);
16 | }
17 |
18 | @Override
19 | protected void onResume() {
20 | super.onResume();
21 | throw new RuntimeException("TestPage2 : onResume 发生了一个异常");
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/lijiaqi/bedrock/test/readme:
--------------------------------------------------------------------------------
1 | test为测试包,可以直接删除。
2 | This a test package(.test), you can just delete it.
--------------------------------------------------------------------------------
/android/app/src/main/java/com/lijiaqi/bedrock/util/ReflexUtil.java:
--------------------------------------------------------------------------------
1 | package com.lijiaqi.bedrock.util;
2 |
3 | import java.lang.reflect.Field;
4 | import java.lang.reflect.Method;
5 |
6 | /**
7 | * @author LiJiaqi
8 | * @date 2020/12/13
9 | * Description:
10 | */
11 |
12 | public class ReflexUtil {
13 |
14 | public static Field getField(Class> clazz, String fieldName){
15 | try {
16 | Field targetField = clazz.getDeclaredField(fieldName);
17 | targetField.setAccessible(true);
18 | return targetField;
19 |
20 | } catch (NoSuchFieldException e) {
21 | e.printStackTrace();
22 | }
23 | return null;
24 | }
25 |
26 |
27 |
28 | public static Method getMethod(Class> clazz, String methodName, String ... params){
29 | try{
30 | Method targetMethod = clazz.getMethod(methodName);
31 | targetMethod.setAccessible(true);
32 | return targetMethod;
33 | } catch (NoSuchMethodException e) {
34 | e.printStackTrace();
35 | }
36 | return null;
37 | }
38 |
39 | private static Field[] getFields(Class> clazz){
40 | try {
41 |
42 | Field[] targetFields = clazz.getDeclaredFields();
43 | for(Field field : targetFields){
44 | field.setAccessible(true);
45 | }
46 | return targetFields;
47 | } catch (Exception e) {
48 | e.printStackTrace();
49 | }
50 | return null;
51 | }
52 |
53 |
54 |
55 | public static void copyTo(T fromObj,T toObj)throws Exception{
56 | if(fromObj == null || toObj == null)throw new NullPointerException();
57 | Field[] fields = getFields(fromObj.getClass());
58 | for(Field field : fields){
59 | field.setAccessible(true);
60 | field.set(toObj, field.get(fromObj));
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/lijiaqi/flutter_bedrock/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.lijiaqi.flutter_bedrock
2 |
3 | import androidx.annotation.NonNull;
4 | import io.flutter.embedding.android.FlutterActivity
5 | import io.flutter.embedding.engine.FlutterEngine
6 | import io.flutter.plugins.GeneratedPluginRegistrant
7 |
8 | class MainActivity: FlutterActivity() {
9 | override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
10 | GeneratedPluginRegistrant.registerWith(flutterEngine);
11 | }
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/layout/activity_test_page1.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
17 |
23 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/activity_test_page2.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/res/xml/provider_paths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
11 |
12 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.6.10'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | // maven { url 'https://maven.aliyun.com/repository/google' }
7 | // maven { url 'https://maven.aliyun.com/repository/jcenter' }
8 | // maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }
9 | }
10 |
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:4.2.0'
13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | mavenCentral()
21 | // maven { url 'https://maven.aliyun.com/repository/google' }
22 | // maven { url 'https://maven.aliyun.com/repository/jcenter' }
23 | // maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }
24 | }
25 | }
26 |
27 | rootProject.buildDir = '../build'
28 | subprojects {
29 | project.buildDir = "${rootProject.buildDir}/${project.name}"
30 | }
31 | subprojects {
32 | project.evaluationDependsOn(':app')
33 | }
34 |
35 | task clean(type: Delete) {
36 | delete rootProject.buildDir
37 | }
38 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableR8=true
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
7 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/android/settings_aar.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/assets/images/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/assets/images/bg.png
--------------------------------------------------------------------------------
/assets/images/car.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/assets/images/car.png
--------------------------------------------------------------------------------
/assets/images/icons/icon_back_black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/assets/images/icons/icon_back_black.png
--------------------------------------------------------------------------------
/assets/images/icons/suggestion_camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/assets/images/icons/suggestion_camera.png
--------------------------------------------------------------------------------
/assets/images/sds.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/assets/images/sds.png
--------------------------------------------------------------------------------
/assets/test_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/assets/test_icon.png
--------------------------------------------------------------------------------
/build.yaml:
--------------------------------------------------------------------------------
1 | targets:
2 | $default:
3 | builders:
4 | reflectable:
5 | generate_for:
6 | - lib/main.dart
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
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/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/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/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/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/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/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/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/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/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/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/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/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/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/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/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/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/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/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/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/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/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/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/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/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/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/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/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/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/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/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/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/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 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | flutter_bedrock
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 | NSCameraUsageDescription
45 | App想要访问照相机权限
46 | NSPhotoLibraryUsageDescription
47 | App想要访问照相册权限
48 |
49 |
50 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
--------------------------------------------------------------------------------
/lib/base_framework/config/app_config.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | import 'package:flutter_bedrock/base_framework/config/storage_manager.dart';
9 |
10 | class AppConfig{
11 |
12 |
13 | static init()async{
14 |
15 | ///启动本地存储工具
16 | await StorageManager.init();
17 |
18 |
19 |
20 |
21 |
22 | }
23 | }
--------------------------------------------------------------------------------
/lib/base_framework/config/frame_constant.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | class BaseFrameConstant{
5 | static const String APP_NAME = "BedRock";
6 | static const String APP_NAME_CN = "BedRock";
7 |
8 | static const String DEVICE_UUID = "deviceUUID";
9 | }
--------------------------------------------------------------------------------
/lib/base_framework/config/global_provider_manager.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | /*
4 | * 全局性provider
5 | * 可以在此配置 全局性model
6 | * eg: user.model et.
7 | *
8 | * */
9 |
10 |
11 | import 'package:flutter_bedrock/base_framework/view_model/app_model/app_cache_model.dart';
12 | import 'package:flutter_bedrock/base_framework/view_model/app_model/device_model.dart';
13 | import 'package:flutter_bedrock/base_framework/view_model/app_model/locale_model.dart';
14 | import 'package:flutter_bedrock/base_framework/view_model/app_model/user_view_model.dart';
15 | import 'package:provider/provider.dart';
16 | import 'package:provider/single_child_widget.dart';
17 |
18 |
19 | List providers =[
20 | ...independentServices,
21 | ...dependentServices,
22 | ];
23 |
24 | /// 应用级 独立 model(通过consumer 可以在任意页面获取到)
25 | List independentServices = [
26 | //ChangeNotifierProvider.value(value: ThemeModel()), //主题配置
27 | ChangeNotifierProvider.value(value: LocaleModel()), //国际化
28 | //设备model
29 | ChangeNotifierProvider.value(value: DeviceModel()),//设备model
30 | //global app cache model
31 | ChangeNotifierProvider.value(value: AppCacheModel()),//项目缓存model
32 | // ///这里应该放入一个购物车
33 | // ChangeNotifierProvider.value(value:
34 | // GlobalCartGoodsModel()),
35 | ///2020.3.10 目前应该没有购物车等可以与用户绑定,这里将用户model抽到上层
36 | ChangeNotifierProvider.value(value: UserViewModel()),//用户 model
37 |
38 | ];
39 |
40 |
41 | /// 需要依赖的model,下方注释代码为例子
42 | /// eg :UserModel 购物车model的组合(如购物车与用户ID绑定)
43 | List dependentServices = [
44 |
45 | // ChangeNotifierProxyProvider(
46 | // update: (context, globalCartGoodsModel, userModel) =>
47 | // userModel ?? UserModel(globalCartGoodsModel: globalCartGoodsModel),
48 | // ),
49 | //
50 | // ChangeNotifierProxyProvider(
51 | // update: (context,userModel,storeModel)
52 | // => storeModel ?? StoreModel(userModel: userModel,cartGoodsModel: userModel.globalCartGoodsModel),
53 | // ),
54 | ];
55 |
56 |
--------------------------------------------------------------------------------
/lib/base_framework/config/net/base_http.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'dart:io';
3 |
4 | import 'package:dio/dio.dart';
5 | import 'package:dio/native_imp.dart';
6 | import 'package:flustars/flustars.dart';
7 | import 'package:flutter/foundation.dart';
8 | import 'package:flutter_bedrock/base_framework/utils/platform_utils.dart';
9 |
10 | import '../frame_constant.dart';
11 |
12 | // 必须是顶层函数
13 | _parseAndDecode(String response) {
14 | return jsonDecode(response);
15 | }
16 |
17 | parseJson(String text) {
18 | return compute(_parseAndDecode, text);
19 | }
20 |
21 | //具体应用配置http类继承此类
22 | abstract class BaseHttp extends DioForNative {
23 | final CancelToken rootCancelToken = CancelToken();
24 |
25 | BaseHttp() {
26 | ///json 解析放在了子isolate,不过按文档来看,这个解析速度要慢于直接解析
27 | ///优点就是不会造成ui卡顿(前提是你的json数据非常大)
28 | ///需要自身来评估
29 | (transformer as DefaultTransformer).jsonDecodeCallback = parseJson;
30 | interceptors..add(new HeaderInterceptor());
31 |
32 | init();
33 | }
34 |
35 | void init();
36 |
37 | cancelAllRequest() {
38 | _cancelToken.cancel(['no available net']);
39 | }
40 | }
41 |
42 | ///默认项目所有cancelToken使用这一个,用于断网下取消
43 | ///如有特殊需要可以 在ApiInterceptor进行覆盖或者注释
44 | final CancelToken _cancelToken = CancelToken();
45 |
46 | ///添加拦截器
47 | class HeaderInterceptor extends InterceptorsWrapper {
48 | @override
49 | void onRequest(
50 | RequestOptions options, RequestInterceptorHandler handler) async {
51 | options.connectTimeout = 1000 * 45;
52 | options.receiveTimeout = 1000 * 45;
53 |
54 | options.cancelToken = _cancelToken;
55 |
56 | ///这里加入版本信息 在header,可以根据需求更改
57 | var appVersion = await PlatformUtils.getAppVersion();
58 | var deviceInfo = SpUtil.getString(BaseFrameConstant.DEVICE_UUID);
59 |
60 | //var token = CookieManager.getCookies(cookies);
61 |
62 | var version = Map()
63 | ..addAll({
64 | 'appVersion': appVersion,
65 | });
66 | options.headers['version'] = version;
67 | options.headers['platform'] = Platform.operatingSystem;
68 | options.headers['clint_id'] = deviceInfo;
69 | super.onRequest(options, handler);
70 | }
71 | }
72 |
73 | /// 子类需要重写
74 | abstract class BaseResponseData {
75 | int? code = 0;
76 | String? message;
77 | dynamic data;
78 |
79 | bool get success;
80 |
81 | BaseResponseData({this.code, this.message, this.data});
82 |
83 | @override
84 | String toString() {
85 | return 'BaseRespData{code: $code, message: $message, data: $data}';
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/lib/base_framework/config/net/bedrock_http.dart:
--------------------------------------------------------------------------------
1 | import 'package:cookie_jar/cookie_jar.dart';
2 | import 'package:dio/dio.dart';
3 | import 'package:dio_cookie_manager/dio_cookie_manager.dart';
4 | import 'package:flutter_bedrock/base_framework/config/net/base_http.dart';
5 | import 'package:flutter_bedrock/base_framework/config/storage_manager.dart';
6 | import 'package:flutter_bedrock/base_framework/view_model/handle/exception_handler.dart';
7 | import 'package:flutter_bedrock/base_framework/view_model/view_state_model.dart';
8 |
9 | import '../../utils/exception_pitcher.dart';
10 |
11 | final BedRock bedRock = BedRock();
12 |
13 | class BedRock extends BaseHttp {
14 | final String china = "https://wanandroid.com/";
15 |
16 | @override
17 | void init() {
18 | options.baseUrl = china;
19 | interceptors
20 | ..add(CookieManager(PersistCookieJar(
21 | storage: FileStorage(StorageManager.appDirectory.path))))
22 | ..add(ApiInterceptor())
23 | ..add(LogInterceptor());
24 | }
25 | }
26 |
27 | class ApiInterceptor extends InterceptorsWrapper {
28 | @override
29 | void onRequest(
30 | RequestOptions options, RequestInterceptorHandler handler) async {
31 | ///这里将空值参数去除掉,可根据自己的需求更改
32 | // options.queryParameters.removeWhere((key, value) => value == null);
33 | //
34 | // String params="";
35 | // String mark = "&";
36 | //
37 | // if(!kReleaseMode){
38 | // debugPrint('---api-request--->url--> ${options.baseUrl}${options.path}' +
39 | // ' queryParameters: ${options.queryParameters}'
40 | // ' formdata : ${options.data.toString()}' );
41 | // options.queryParameters.forEach((k,v){
42 | // if(v == null) return;
43 | // params = "$params${params.isEmpty?"":mark}$k=$v";
44 | // });
45 | // debugPrint("---api-request--->url--> ${options.baseUrl}${options.path}?$params");
46 | // }
47 |
48 | //debugPrint("request header : ${options.headers.toString()}");
49 | super.onRequest(options, handler);
50 | }
51 |
52 | ///这里可以根据不同的业务代码 扔出不同的异常
53 | ///具体要根据后台进行协商
54 | /// [ViewStateModel] 的子类会对此处进行捕捉,捕捉后逻辑可以在[ExceptionHandler]中处理
55 | /// * 此处的异常捕捉功能仅在[loadData]中有效
56 | /// * 如果需要独立收到Api的业务异常,见此类[ExceptionBinding]
57 |
58 | @override
59 | void onResponse(Response response, ResponseInterceptorHandler handler) {
60 | ResponseData responseData = ResponseData.fromJson(response.data);
61 | if (responseData.success) {
62 | return super.onResponse(response, handler);
63 | } else {
64 | ///抛出业务异常
65 | throw ExceptionPitcher().transformException(responseData);
66 | }
67 | }
68 | }
69 |
70 | class ResponseData extends BaseResponseData {
71 | bool get success => (code == 1 || code == 200);
72 |
73 | ResponseData.fromJson(Map json) {
74 | code = json['code'];
75 | message = json['message'];
76 | data = json['data'];
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/lib/base_framework/config/storage_manager.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flustars/flustars.dart';
4 | import 'package:path_provider/path_provider.dart';
5 |
6 | class StorageManager {
7 | /// 临时目录 (华为手机可能会随机删除这个目录下的文件)
8 | static Directory? temporaryDirectory;
9 |
10 | ///应用目录
11 | static late Directory appDirectory;
12 |
13 | ///外部存储 (仅限安卓)
14 | static Directory? externalDirectory;
15 |
16 | static init() async {
17 | await SpUtil.getInstance();
18 | temporaryDirectory = await getTemporaryDirectory();
19 | appDirectory = await getApplicationDocumentsDirectory();
20 | if (Platform.isAndroid) {
21 | externalDirectory = await getExternalStorageDirectory();
22 | }
23 |
24 | ///本地缓存基本都可以使用此工具
25 | ///后续页面可以同步使用
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/base_framework/constant.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/3/24
4 | */
5 |
6 |
7 |
8 |
9 |
10 |
11 | class Constant {
12 |
13 | }
--------------------------------------------------------------------------------
/lib/base_framework/empty.dart:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/lib/base_framework/empty.dart
--------------------------------------------------------------------------------
/lib/base_framework/exception/base_exception.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ///异常基类
5 | /// * 建议项目内异常都继承自此异常,方便后期管理
6 | abstract class BaseException implements Exception{
7 | String message = "";
8 |
9 | bool _handled = false;
10 |
11 | BaseException(this.message);
12 |
13 | /// 是否已处理
14 | /// * 可以基于此值避免重复处理异常
15 | /// * 复写此方法可以调整"重复"判断逻辑
16 | bool handled() => _handled;
17 |
18 | @override
19 | String toString() {
20 | return "${this.runtimeType} : $message";
21 | }
22 | }
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/lib/base_framework/exception/data_missing_exception.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'package:flutter_bedrock/base_framework/exception/base_exception.dart';
4 |
5 | /// data missing
6 |
7 | class DataMissingException extends BaseException{
8 | DataMissingException({String message = "data missing"}) : super(message);
9 |
10 | }
--------------------------------------------------------------------------------
/lib/base_framework/exception/un_authorized_exception.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'package:flutter_bedrock/base_framework/exception/base_exception.dart';
4 |
5 | /// 用于未登录等权限不够,需要跳转授权页面
6 | class UnAuthorizedException extends BaseException {
7 |
8 |
9 | UnAuthorizedException({String message = "unAuthorizedException"}) : super(message) ;
10 | }
--------------------------------------------------------------------------------
/lib/base_framework/exception/un_handle_exception.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/5/15
4 | */
5 |
6 |
7 |
8 | import 'package:flutter_bedrock/base_framework/exception/base_exception.dart';
9 |
10 | class UnHandleException extends BaseException{
11 |
12 | UnHandleException(String message) : super(message);
13 |
14 |
15 | }
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/lib/base_framework/exception/user_unbind_exception.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/5/9
4 | */
5 |
6 | import 'package:flutter_bedrock/base_framework/exception/base_exception.dart';
7 |
8 | class UserUnbindException extends BaseException{
9 | UserUnbindException(String message) : super(message);
10 |
11 |
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/lib/base_framework/exception/verification_code_exception.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'package:flutter_bedrock/base_framework/exception/base_exception.dart';
4 |
5 | class VerificationCodeException extends BaseException {
6 | VerificationCodeException(String message) : super(message);
7 |
8 | }
--------------------------------------------------------------------------------
/lib/base_framework/extension/list_extension.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/4/2
4 | */
5 |
6 | import 'package:flutter/cupertino.dart';
7 |
8 | ///extension 可以对原有类进行扩展,下面实现了 list.toString 后,去除中括号
9 |
10 | extension ListToString on List{
11 |
12 | ///将list转为以‘,’分隔的字符串
13 | String toStringByComma(){
14 | //[a,b,c] => a,b,c
15 | //[a] => a
16 | String list = this.toString();//[]
17 | debugPrint("list.toString : $list");
18 | //list.replaceAll(" ", "");
19 | debugPrint("list replace all /b : $list");
20 | return list.length <= 2 ? "" :list.substring(1,list.length-1);
21 | }
22 | }
--------------------------------------------------------------------------------
/lib/base_framework/extension/size_adapter_extension.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'package:flustars/flustars.dart';
4 | import 'package:flutter/material.dart';
5 |
6 | extension SizeAdapterExtension on num{
7 |
8 | ///获取适配后的宽度
9 | double get w => ScreenUtil.getInstance().getWidthPx(this.toDouble());
10 |
11 | ///获取适配后的高度
12 | double get h => ScreenUtil.getInstance().getHeightPx(this.toDouble());
13 |
14 | ///获取适配后的文本尺寸
15 | double get sp => ScreenUtil.getInstance().getSp(this.toDouble());
16 |
17 | ///获取一个用于纵向占位的[SizedBox]
18 | /// * eg. [Column] 的空间占位
19 | SizedBox get vGap => SizedBox(width: 1,height: w,);
20 |
21 | ///获取一个用于横向占位的sizeBox[SizedBox]
22 | /// * eg. [Row] 的空间占位
23 | SizedBox get hGap => SizedBox(width: w,height: 1,);
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 |
--------------------------------------------------------------------------------
/lib/base_framework/factory/page/page_animation_builder.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_bedrock/base_framework/ui/anim/page_route_anim/fade_animation.dart';
5 | import 'package:flutter_bedrock/base_framework/ui/anim/page_route_anim/no_animation.dart';
6 | import 'package:flutter_bedrock/base_framework/ui/anim/page_route_anim/size_scale_animation.dart';
7 | import 'package:flutter_bedrock/base_framework/ui/anim/page_route_anim/slide_animation.dart';
8 |
9 |
10 | final PageAnimationBuilder? pageBuilder = PageAnimationBuilder.getInstance();
11 |
12 |
13 | ///动画枚举类,这个你可以根据需求自定义
14 | ///但最好保持代码这个结构
15 |
16 | enum PageAnimation{
17 | Fade,Scale,Slide,Non
18 | }
19 |
20 |
21 | class PageAnimationBuilder{
22 |
23 | static PageAnimationBuilder? singleton;
24 |
25 | static PageAnimationBuilder? getInstance(){
26 | if(singleton == null){
27 | singleton = PageAnimationBuilder._();
28 | }
29 | return singleton;
30 | }
31 |
32 | PageAnimationBuilder._();
33 |
34 |
35 | ///
36 |
37 | Route wrapWithNoAnim(Widget page,RouteSettings routeSettings){
38 | return NoAnimRouteBuilder(page,routeSettings);
39 | }
40 |
41 |
42 | ///fade
43 | Route wrapWithFadeAnim(Widget page,RouteSettings routeSettings){
44 | return FadeRouteBuilder(page,routeSettings);
45 | }
46 |
47 |
48 | ///slide
49 | Route wrapWithSlideAnim(Widget page,RouteSettings routeSettings){
50 | return SlideRightRouteBuilder(page,routeSettings);
51 | }
52 |
53 | Route wrapWithSlideTopAnim(Widget page,RouteSettings routeSettings){
54 | return SlideTopRouteBuilder(page,routeSettings);
55 | }
56 |
57 |
58 | ///scale
59 | Route wrapWithScaleAnim(Widget page,RouteSettings routeSettings){
60 | return ScaleRouteBuilder(page,routeSettings);
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 |
--------------------------------------------------------------------------------
/lib/base_framework/factory/useless.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ///useless
--------------------------------------------------------------------------------
/lib/base_framework/native/native_method_manager.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/7/6
4 | */
5 |
6 |
7 | import 'dart:io';
8 |
9 | import 'package:flutter/services.dart';
10 |
11 | class NativeMethodManager{
12 |
13 | static const MethodChannel _channel = const MethodChannel('com.lijiaqi.bedrock');
14 |
15 | static final String methodInstall = 'install_apk';
16 |
17 |
18 | static NativeMethodManager? _singleton;
19 |
20 | static NativeMethodManager? getInstance(){
21 | if(_singleton == null){
22 | _singleton = NativeMethodManager._();
23 | }
24 | return _singleton;
25 | }
26 | NativeMethodManager._();
27 |
28 |
29 | ///安卓端:安装指定路径的apk,如果有问题请给我提issue
30 |
31 | void installApk(String path)async{
32 | ///ios建议直接取应用市场
33 | if(isAndroid()){
34 | await _channel.invokeMethod(methodInstall,
35 | {"path":path});
36 | }
37 | }
38 |
39 | ///=====================Test===============================
40 | ///android端异常测试
41 | static final String childThreadException = 'child_exception';
42 | static final String uIThreadException = 'ui_exception';
43 | static final String startUpException = 'start_up_exception';
44 |
45 | void throwChildThreadException()async{
46 | if(isAndroid()){
47 | await _channel.invokeMethod(childThreadException);
48 | }
49 | }
50 |
51 | void throwUiThreadException()async{
52 | if(isAndroid()){
53 | await _channel.invokeMethod(uIThreadException);
54 | }
55 | }
56 |
57 | void throwStartUpException()async{
58 | if(isAndroid()){
59 | await _channel.invokeMethod(startUpException);
60 | }
61 | }
62 |
63 | bool isAndroid(){
64 | return Platform.isAndroid;
65 | }
66 |
67 |
68 | }
--------------------------------------------------------------------------------
/lib/base_framework/observe/app_status/app_status_observe.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/8/27
4 | *
5 | * 建议保持这种创建方式,分离子线程和主线程文件
6 | */
7 |
8 | export 'net_observer.dart';
9 | export 'server_notify_observer.dart';
--------------------------------------------------------------------------------
/lib/base_framework/observe/app_status/net_observer.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/8/28
4 | */
5 |
6 | import 'dart:async';
7 | import 'dart:io';
8 | import 'dart:isolate';
9 |
10 | import 'package:flutter/cupertino.dart';
11 | import 'package:flutter/material.dart';
12 |
13 |
14 |
15 | /*
16 | * 注意,此方法在child_isolate
17 | * 返回值为List
18 | * */
19 |
20 | /// 第一个元素等于此key,则第二个元素为 child isolate 的sendPort
21 | const String kNetPortKey = 'kNetPortKey';
22 |
23 | ///第一个元素为[kNetAvailable]
24 | const String kNetAvailable = 'kNetAvailable';
25 | ///第二个元素则为下面的其中之一
26 | const String kNetEnable = 'kEnable',kNetDisable = 'kDisable';
27 |
28 |
29 | const int intervalTime = 10;
30 |
31 | void observerNetState(SendPort sendPort){
32 | final String china = 'baidu.com';
33 | final String usa = 'google.com';
34 | final ReceivePort receivePort = ReceivePort();
35 | receivePort.listen((message) {
36 | debugPrint('$kNetPortKey : $message');
37 | });
38 |
39 | sendPort.send([kNetPortKey,receivePort.sendPort]);
40 |
41 |
42 |
43 | final timer = Timer.periodic(Duration(seconds: intervalTime), (timer) async{
44 | try{
45 | ///注意区分国内外,或者用你自己的
46 | String host = china;
47 | final result = await InternetAddress.lookup(host);
48 | debugPrint('$kNetPortKey $result');
49 | if(result.isNotEmpty && result[0].rawAddress.isNotEmpty){
50 | ///net enable
51 | sendPort.send([kNetAvailable,kNetEnable]);
52 | }else{
53 | sendPort.send([kNetAvailable,kNetDisable]);
54 | }
55 |
56 | }on SocketException catch(_){
57 | sendPort.send([kNetAvailable,kNetDisable]);
58 |
59 | }
60 | });
61 |
62 |
63 | }
--------------------------------------------------------------------------------
/lib/base_framework/observe/app_status/server_notify_observer.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import 'dart:async';
5 | import 'dart:isolate';
6 |
7 | import 'package:flutter/cupertino.dart';
8 |
9 | const kServeNotifyPortKey = 'kServeNotifyPortKey';
10 |
11 | ///对服务器通知进行监测
12 | ///实际上这个功能在主线程实现也没有什么问题,
13 | ///不过一些特殊需求可能放在子线程(隔离 不太顺口)比较好
14 |
15 | void observeServerNotify(SendPort sendPort){
16 |
17 | final ReceivePort receivePort = ReceivePort();
18 |
19 | receivePort.listen((message) {
20 | debugPrint('$kServeNotifyPortKey : $message');
21 | });
22 | sendPort.send(receivePort.sendPort);
23 |
24 | final timer = Timer.periodic(Duration(seconds: 60), (timer) {
25 | ///do your stuff here
26 |
27 | ///var result = requestAPI();
28 | ///sendPort.send([key,result]);
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 |
--------------------------------------------------------------------------------
/lib/base_framework/observe/own_navigator_observe.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/7/17
4 | */
5 |
6 |
7 |
8 | import 'package:flutter/material.dart';
9 |
10 | class OwnNavigatorObserve extends NavigatorObserver{
11 |
12 |
13 | @override
14 | void didReplace({Route? newRoute, Route? oldRoute}) {
15 |
16 | }
17 |
18 | @override
19 | void didRemove(Route route, Route? previousRoute) {
20 |
21 | }
22 |
23 | @override
24 | void didPop(Route route, Route? previousRoute) {
25 | log('pop route', route?.settings?.name);
26 | log('pop previous', previousRoute?.settings?.name);
27 | }
28 |
29 | @override
30 | void didPush(Route route, Route? previousRoute) {
31 | log('push route', route?.settings?.name);
32 | log('push previous', previousRoute?.settings?.name);
33 |
34 | }
35 |
36 | void log(String title,String? info){}
37 | }
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/lib/base_framework/observe/route/router_binding.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/9/9
4 | */
5 |
6 | _RouterManager manager = _RouterManager();
7 |
8 |
9 | ///路由相关
10 | ///可以在此拓展功能,如记录、埋点等等根据自己的需求增加
11 |
12 | abstract class RouterBinding{
13 |
14 |
15 | addRecord(String page){
16 | if(page.isEmpty == null)return;
17 | manager.add(page);
18 | }
19 |
20 | removeRecord(String page){
21 | if(page == null)return;
22 | manager.remove(page);
23 | }
24 | }
25 |
26 |
27 |
28 | class _RouterManager{
29 | final List routeRecords = [];
30 | add(String page){
31 | routeRecords.add(page);
32 | }
33 |
34 | remove(String page){
35 | if(routeRecords.last == page){
36 | routeRecords.removeLast();
37 | }
38 | }
39 |
40 |
41 | }
--------------------------------------------------------------------------------
/lib/base_framework/ui/anim/a.dart:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/lib/base_framework/ui/anim/a.dart
--------------------------------------------------------------------------------
/lib/base_framework/ui/anim/page_route_anim/fade_animation.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import 'package:flutter/material.dart';
5 |
6 | class FadeRouteBuilder extends PageRouteBuilder {
7 | final Widget page;
8 | final RouteSettings routeSettings;
9 |
10 | FadeRouteBuilder(this.page,this.routeSettings)
11 | : super(settings:routeSettings,
12 | pageBuilder: (context, animation, secondaryAnimation) => page,
13 | transitionDuration: Duration(milliseconds: 500),
14 | transitionsBuilder: (context, animation, secondaryAnimation,
15 | child) =>
16 | FadeTransition(
17 | opacity: Tween(begin: 0.1, end: 1.0).animate(CurvedAnimation(
18 | parent: animation,
19 | curve: Curves.fastOutSlowIn,
20 | )),
21 | child: child,
22 | ));
23 | }
--------------------------------------------------------------------------------
/lib/base_framework/ui/anim/page_route_anim/no_animation.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class NoAnimRouteBuilder extends PageRouteBuilder {
4 | final Widget page;
5 | final RouteSettings routeSettings;
6 |
7 | NoAnimRouteBuilder(this.page,this.routeSettings)
8 | : super(settings:routeSettings,
9 | opaque: false,
10 | pageBuilder: (context, animation, secondaryAnimation) => page,
11 | transitionDuration: Duration(milliseconds: 0),
12 | transitionsBuilder:
13 | (context, animation, secondaryAnimation, child) => child);
14 | }
--------------------------------------------------------------------------------
/lib/base_framework/ui/anim/page_route_anim/size_scale_animation.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | class ScaleRouteBuilder extends PageRouteBuilder {
6 | final Widget page;
7 | final RouteSettings routeSettings;
8 |
9 | ScaleRouteBuilder(this.page,this.routeSettings)
10 | : super(settings:routeSettings,
11 | pageBuilder: (context, animation, secondaryAnimation) => page,
12 | transitionDuration: Duration(milliseconds: 300),
13 | transitionsBuilder: (context, animation, secondaryAnimation, child) =>
14 | // Align(
15 | // child: SizeTransition(child: child, sizeFactor: animation),
16 | // ),
17 | ScaleTransition(
18 | scale: Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
19 | parent: animation, curve: Curves.fastOutSlowIn)),
20 | child: child,
21 | ),
22 | );
23 | }
--------------------------------------------------------------------------------
/lib/base_framework/ui/anim/page_route_anim/slide_animation.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | class SlideTopRouteBuilder extends PageRouteBuilder {
6 | final Widget page;
7 | final RouteSettings routeSettings;
8 |
9 | SlideTopRouteBuilder(this.page,this.routeSettings)
10 | : super(settings:routeSettings,
11 | pageBuilder: (context, animation, secondaryAnimation) => page,
12 | transitionDuration: Duration(milliseconds: 800),
13 | transitionsBuilder:
14 | (context, animation, secondaryAnimation, child) =>
15 | SlideTransition(
16 | position: Tween(
17 | begin: Offset(0.0, -1.0), end: Offset(0.0, 0.0))
18 | .animate(CurvedAnimation(
19 | parent: animation, curve: Curves.fastOutSlowIn)),
20 | child: child,
21 | ));
22 | }
23 |
24 |
25 | class SlideBottomRouteBuilder extends PageRouteBuilder{
26 | final Widget page;
27 |
28 | SlideBottomRouteBuilder(this.page)
29 | : super(
30 | pageBuilder:(ctx,animation,secondaryAnimation)=>page,
31 | transitionDuration:Duration(milliseconds: 800),
32 | transitionsBuilder:(ctx,animation,secondaryAnimation,child)
33 | => SlideTransition(
34 | position: Tween(
35 | begin: Offset(0.0,1.0),
36 | end: Offset(0.0,0.0),
37 | ).animate(CurvedAnimation(
38 | parent: animation,curve: Curves.fastOutSlowIn
39 | )),
40 | child: child,
41 | )
42 | );
43 |
44 |
45 |
46 | }
47 |
48 | ///右滑进入,同时如果需要左侧滑动退出的,请使用此动画
49 |
50 | class SlideRightRouteBuilder extends PageRouteBuilder{
51 | final Widget page;
52 | final RouteSettings routeSettings;
53 |
54 |
55 | SlideRightRouteBuilder(this.page,this.routeSettings)
56 | :super(settings:routeSettings,
57 | pageBuilder:(ctx,animation,secondaryAnimation)=>page,
58 | opaque:false,
59 | transitionDuration:Duration(milliseconds: 300),
60 | transitionsBuilder:(ctx,animation,secondaryAnimation,child)
61 | => SlideTransition(
62 | position: Tween(
63 | begin: Offset(1.0,0.0),
64 | end: Offset(0.0,0.0),
65 | ).animate(CurvedAnimation(
66 | parent: animation,curve: Curves.fastOutSlowIn
67 | )),
68 | child: child,
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 |
--------------------------------------------------------------------------------
/lib/base_framework/ui/behavior/over_scroll_behavior.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | /// dislodge the any subclass of ScrollView's inkwell
5 | /// 去除scroll view的 水印
6 |
7 | class OverScrollBehavior extends ScrollBehavior {
8 | @override
9 | Widget buildViewportChrome(
10 | BuildContext context, Widget child, AxisDirection axisDirection) {
11 | switch (getPlatform(context)) {
12 | case TargetPlatform.iOS:
13 | return child;
14 | case TargetPlatform.fuchsia:
15 | case TargetPlatform.android:
16 | return GlowingOverscrollIndicator(
17 | child: child,
18 | //do not show head's inkwell
19 | showLeading: false,
20 | //do not show tail's inkwell
21 | showTrailing: false,
22 | axisDirection: axisDirection,
23 | color: Theme.of(context).accentColor,
24 | );
25 | case TargetPlatform.linux:
26 | case TargetPlatform.macOS:
27 | case TargetPlatform.windows:
28 | }
29 | return super.buildViewportChrome(context, child, axisDirection);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib/base_framework/ui/empty.dart:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bladeofgod/Bedrock/e426d04c678487e0e03af767eb1c71f5db31519b/lib/base_framework/ui/empty.dart
--------------------------------------------------------------------------------
/lib/base_framework/ui/widget/activity_indicator.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | /// 由于app不管明暗模式,都是有底色
5 | /// 所以将indicator颜色为亮色
6 | class ActivityIndicator extends StatelessWidget {
7 | final double? radius;
8 | final Brightness? brightness;
9 |
10 | ActivityIndicator({this.radius, this.brightness});
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | return Theme(
15 | data: ThemeData(
16 | cupertinoOverrideTheme: CupertinoThemeData(brightness: brightness),
17 | ),
18 | child: CupertinoActivityIndicator(radius: radius ?? 10));
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lib/base_framework/ui/widget/circle_border_container.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/3/31
4 | */
5 |
6 |
7 | import 'package:flutter/material.dart';
8 | import 'package:flutter_bedrock/base_framework/widget_state/base_stateless_widget.dart';
9 |
10 |
11 | class CircleBorderContainer extends BaseStatelessWidget{
12 | final double? width,height;
13 | final Color? borderColor;
14 | final double? borderWidth;
15 | final String? text;
16 | final TextStyle? style;
17 |
18 |
19 | CircleBorderContainer({this.width, this.height, this.borderColor,
20 | this.borderWidth, this.text,this.style});
21 |
22 | @override
23 | Widget build(BuildContext context) {
24 |
25 | return Container(
26 | alignment: Alignment.center,
27 | width: width,
28 | height: height,
29 | decoration: BoxDecoration(
30 | shape: BoxShape.circle,
31 | border: Border.all(width: borderWidth!,color: borderColor!)
32 | ),
33 | child: Text(text!,style: style,),
34 | );
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/lib/base_framework/ui/widget/detail_image_widget.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/4/17
4 | */
5 |
6 | import 'package:extended_image/extended_image.dart';
7 | import 'package:flutter/material.dart';
8 |
9 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
10 |
11 |
12 | class DetailImageWidgetState extends PageState {
13 |
14 | final List? imageList;
15 | final int initIndex;
16 | int indexStr=1;
17 |
18 | DetailImageWidgetState(this.imageList, {this.initIndex = 0});
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 |
23 | return switchStatusBar2Dark(
24 | isSetDark: true,
25 | edgeInsets: EdgeInsets.all(0),
26 | child: Container(
27 | width: getWidthPx(750),
28 | height: getHeightPx(1334),
29 | child: Stack(
30 | children: [
31 | Container(
32 | color: Colors.black,
33 | width: getWidthPx(750),
34 | height: getHeightPx(1334),
35 | ///可增加滑动退出等功能、具体可以查看插件的文档
36 | child: ExtendedImageGesturePageView.builder(
37 | controller: new ExtendedPageController(initialPage: this.initIndex),
38 | itemCount: imageList!.length,
39 | itemBuilder: (ctx,index){
40 | var url = "${imageList![index]}";
41 | //var url = "http://a0.att.hudong.com/78/52/01200000123847134434529793168.jpg";
42 | Widget image = ExtendedImage.network(
43 | url,fit: BoxFit.contain,mode: ExtendedImageMode.gesture,
44 | initGestureConfigHandler: (state){
45 | return GestureConfig(
46 | inPageView: imageList!.length>1
47 | );
48 | },
49 | );
50 | return image;
51 | },
52 | onPageChanged: (int index) {
53 | setState(() {
54 | indexStr=index+1;
55 | });
56 | },
57 | ),
58 | ),
59 | commonAppBar(
60 | title: "$indexStr/${imageList!.length}",
61 | leftWidget: buildAppBarLeft(),
62 | leftPadding: getWidthPx(40),
63 | rightPadding: getWidthPx(40)),
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 |
--------------------------------------------------------------------------------
/lib/base_framework/ui/widget/float_container_widget.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/12/4
4 | * 浮层,用于承载类似对话框的widget
5 | *
6 | *
7 | * 基础性浮层 :仅提供一个 container,具体复现内容由你定。
8 | */
9 |
10 | import 'package:flutter/cupertino.dart';
11 | import 'package:flutter_bedrock/base_framework/widget_state/widget_state.dart';
12 |
13 |
14 | typedef FloatWidgetDismiss = void Function(BuildContext context);
15 |
16 | ///浮层(页面)的 [RouteSettings].name
17 | /// * 某些情况,可能需要当前route的名字,故这里标记上。
18 | final String floatLayerRouteName = 'FloatContainerWidget';
19 |
20 | class FloatContainerWidget extends WidgetState{
21 |
22 | ///背景颜色
23 | final Color? bgColor;
24 | final Widget child;
25 | ///对齐方式
26 | final Alignment? alignment;
27 | ///是否点击背景可以退出
28 | final bool? barrierDismissible;
29 | ///‘pop’ 退出 外置
30 | ///例如,我们需要做一个动画后再弹出。
31 | final FloatWidgetDismiss floatWidgetDismiss;
32 |
33 | FloatContainerWidget(this.child,{required this.floatWidgetDismiss,this.bgColor,this.alignment,this.barrierDismissible})
34 | :assert(child != null),assert(floatWidgetDismiss != null);
35 |
36 |
37 | @override
38 | Widget build(BuildContext context) {
39 | final Size size = MediaQuery.of(context).size;
40 | return GestureDetector(
41 | onTap: (){
42 | if(barrierDismissible!){
43 | floatWidgetDismiss(context);
44 | }
45 | },
46 | child: Container(
47 | color: bgColor,
48 | width: size.width,height: size.height,
49 | alignment:alignment,
50 | child:child,
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 |
--------------------------------------------------------------------------------
/lib/base_framework/ui/widget/no_ink_well_factory.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /*
4 | * Theme(
5 | * data: ThemeData(splashFactory: NoInkWellFactory()),
6 | * child: childWidget,
7 | * );
8 | *
9 | *
10 | * */
11 |
12 | class NoInkWellFactory extends InteractiveInkFeatureFactory {
13 | @override
14 | InteractiveInkFeature create(
15 | {required MaterialInkController controller,
16 | required RenderBox referenceBox,
17 | Offset? position,
18 | required Color color,
19 | TextDirection? textDirection,
20 | bool containedInkWell = false,
21 | rectCallback,
22 | BorderRadius? borderRadius,
23 | ShapeBorder? customBorder,
24 | double? radius,
25 | onRemoved}) {
26 | return NoInkWell(
27 | color: color, controller: controller, referenceBox: referenceBox);
28 | }
29 | }
30 |
31 | class NoInkWell extends InteractiveInkFeature {
32 | NoInkWell(
33 | {required Color color,
34 | required MaterialInkController controller,
35 | required RenderBox referenceBox})
36 | : assert(controller != null),
37 | assert(referenceBox != null),
38 | super(color: color, controller: controller, referenceBox: referenceBox);
39 |
40 | @override
41 | void paintFeature(Canvas canvas, Matrix4 transform) {}
42 | }
43 |
--------------------------------------------------------------------------------
/lib/base_framework/ui/widget/notification/common_notification_widget.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_bedrock/base_framework/widget_state/widget_state.dart';
6 |
7 | typedef NotifyDone = void Function(bool notifyDone);
8 |
9 | /// 通知显示类型的widget(包裹层) 建议都在这个文件夹下创建
10 |
11 | class FromTopNotifyWidget extends WidgetState with SingleTickerProviderStateMixin {
12 |
13 |
14 | final Widget child;
15 | final Duration animationDuration;
16 | final Duration notifyDwellTime;
17 | final NotifyDone notifyDone;
18 |
19 | AnimationController? controller;
20 | late Animation animation;
21 |
22 | FromTopNotifyWidget(this.child, this.animationDuration, this.notifyDwellTime,this.notifyDone);
23 |
24 |
25 | @override
26 | void initState() {
27 | // TODO: implement initState
28 | super.initState();
29 | controller = AnimationController(vsync: this,duration: animationDuration);
30 | animation = Tween(begin: Offset(0,-1),end: Offset.zero).animate(controller!);
31 | controller!.addStatusListener((status) {
32 | if(status == AnimationStatus.completed){
33 | Future.delayed(notifyDwellTime)
34 | .whenComplete(() => controller?.reverse()?.whenComplete(() => notifyDone(true)));
35 | }
36 | });
37 | WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
38 | controller!.forward();
39 | });
40 | }
41 | @override
42 | void dispose() {
43 | controller?.dispose();
44 | // TODO: implement dispose
45 | super.dispose();
46 | }
47 |
48 | @override
49 | Widget build(BuildContext context) {
50 | return Column(
51 | children: [AnimatedBuilder(
52 | animation: animation,
53 | builder: (ctx,c){
54 | return SlideTransition(
55 | position:animation as Animation ,
56 | child: child,);
57 | },
58 | )],
59 | );
60 | }
61 |
62 | }
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/lib/base_framework/ui/widget/route/pop_window_builder.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import 'package:flutter/cupertino.dart';
5 | import 'package:flutter/semantics.dart';
6 |
7 | typedef PopWidgetBuilder = Widget Function(BuildContext context);
8 | ///路由将无法观测到
9 | class PopWindowBuilder extends OverlayRoute{
10 |
11 | final PopWidgetBuilder builder;
12 | final bool maintainState;
13 |
14 | PopWindowBuilder(this.builder,{this.maintainState = true});
15 |
16 |
17 | // We cache the part of the modal scope that doesn't change from frame to
18 | // frame so that we minimize the amount of building that happens.
19 | Widget? _modalScopeCache;
20 |
21 |
22 |
23 | // one of the builders
24 | Widget _buildModalScope(BuildContext context) {
25 | // To be sorted before the _modalBarrier.
26 | return _modalScopeCache ??= Semantics(
27 | sortKey: const OrdinalSortKey(0.0),
28 | child: Builder(builder: (ctx){
29 | return builder(ctx);
30 | })
31 | );
32 | }
33 |
34 | OverlayEntry? _modalScope;
35 |
36 | @override
37 | Iterable createOverlayEntries()sync* {
38 | yield _modalScope = OverlayEntry(builder: _buildModalScope, maintainState: maintainState);
39 | }
40 | }
--------------------------------------------------------------------------------
/lib/base_framework/ui/widget/route/route_aware_widget.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | /*
7 | * 2020年9月9日16:12:01
8 | *
9 | * 路由改版后,此widget废弃
10 | *
11 | * */
12 |
13 | //class RouteAwareWidget extends StatefulWidget{
14 | //
15 | // final String name;
16 | // final Widget child;
17 | //
18 | //
19 | // RouteAwareWidget(this.name, this.child);
20 | //
21 | // @override
22 | // State createState() {
23 | // return RouteAwareWidgetState();
24 | // }
25 | //
26 | //}
27 | //
28 | //class RouteAwareWidgetState extends BaseState with RouteAware {
29 | //
30 | // @override
31 | // void didChangeDependencies() {
32 | // super.didChangeDependencies();
33 | // routeObserver.subscribe(this, ModalRoute.of(context));
34 | // }
35 | // @override
36 | // void dispose() {
37 | // routeObserver.unsubscribe(this);
38 | // super.dispose();
39 | // }
40 | //
41 | // @override
42 | // void didPush() {
43 | // ///将要进入的页面
44 | // debugPrint("push ${widget.name}");
45 | // super.didPush();
46 | // }
47 | //
48 | // @override
49 | // void didPop() {
50 | // ///将要弹出的页面
51 | // debugPrint("pop ${widget.name}");
52 | // super.didPop();
53 | // }
54 | //
55 | // @override
56 | // void didPopNext() {
57 | // ///弹出后显示的页面
58 | // debugPrint("pop next ${widget.name}");
59 | // super.didPopNext();
60 | // }
61 | //
62 | // @override
63 | // void didPushNext() {
64 | // ///进入后,被遮挡的页面
65 | // debugPrint("push next ${widget.name}");
66 | // super.didPushNext();
67 | // }
68 | //
69 | //
70 | // @override
71 | // Widget build(BuildContext context) {
72 | // return widget.child;
73 | // }
74 | //}
75 | //
76 | //
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/lib/base_framework/ui/widget/skeleton/base_skeleton/base_skeleton_widget.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/4/24
4 | */
5 |
6 | import 'package:flutter/material.dart';
7 | import 'package:flutter_bedrock/base_framework/widget_state/base_stateless_widget.dart';
8 | import 'package:shimmer/shimmer.dart';
9 |
10 | ///骨架屏 建议用stateless
11 |
12 | abstract class BaseSkeletonWidget extends BaseStatelessWidget {
13 | ///确保全局骨架 颜色一致
14 | final Color skeletonColor = Color.fromRGBO(221, 221, 221, 1);
15 | final Color baseColor = Color(0xFFE0E0E0);
16 | final Color highLightColor = Color(0xFFF5F5F5);
17 |
18 | final bool _enabled = true;
19 |
20 | ///自动给child 增加 闪闪亮特效!
21 | Widget getShimmer(Widget child) {
22 | return Shimmer(
23 | gradient: LinearGradient(
24 | begin: Alignment.centerLeft,
25 | end: Alignment.centerRight,
26 | colors: [
27 | baseColor,
28 | baseColor,
29 | highLightColor,
30 | baseColor,
31 | baseColor
32 | ],
33 | stops: const [
34 | 0.0,
35 | 0.35,
36 | 0.5,
37 | 0.65,
38 | 1.0
39 | ]),
40 | // baseColor: Colors.grey[300],
41 | // highlightColor: Colors.grey[100],
42 | enabled: _enabled,
43 | child: child,
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/lib/base_framework/ui/widget/skeleton/skeleton_rect.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/4/24
4 | */
5 |
6 |
7 |
8 |
9 | import 'package:flutter/material.dart';
10 |
11 | import 'base_skeleton/base_skeleton_widget.dart';
12 |
13 |
14 | class SkeletonRect extends BaseSkeletonWidget{
15 |
16 | final double? width;
17 | final double? height;
18 | final double? radius;
19 | final Color? color;
20 |
21 |
22 | SkeletonRect({this.width, this.height, this.radius, this.color});
23 |
24 | @override
25 | Widget build(BuildContext context) {
26 |
27 | return Container(
28 | width: width,height: height,
29 | decoration: BoxDecoration(
30 | color: color??skeletonColor,borderRadius: BorderRadius.circular(radius!)
31 | ),
32 | );
33 | }
34 |
35 | }
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/lib/base_framework/ui/widget/web/html_page.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/3
4 | *
5 | * 用于显示富文本
6 | */
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
10 | import 'package:webview_flutter/webview_flutter.dart';
11 |
12 | class HtmlPageState extends PageState {
13 | final String htmlContent;
14 |
15 | HtmlPageState(this.htmlContent);
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return switchStatusBar2Dark(
20 | child: Container(
21 | width: getWidthPx(750),
22 | height: getHeightPx(1334),
23 | child: Column(
24 | children: [
25 | commonAppBar(
26 | leftWidget: buildAppBarLeft(),
27 | leftPadding: getWidthPx(40),
28 | rightPadding: getWidthPx(40)),
29 | Expanded(
30 | child: WebView(
31 | initialUrl: Uri.dataFromString(htmlContent, mimeType: 'text/html')
32 | .toString(),
33 | ),
34 | ),
35 | ],
36 | ),
37 | ));
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/base_framework/ui/widget/web/web_page.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/3
4 | *
5 | * 普通网页展示,需要的也可以自定义
6 | */
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
10 | import 'package:webview_flutter/webview_flutter.dart';
11 |
12 | class WebPageState extends PageState {
13 | final String url;
14 |
15 | WebPageState(this.url);
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return switchStatusBar2Dark(
20 | child: Container(
21 | width: getWidthPx(750),
22 | height: getHeightPx(1334),
23 | child: Column(
24 | children: [
25 | commonAppBar(
26 | leftWidget: buildAppBarLeft(),
27 | leftPadding: getWidthPx(40),
28 | rightPadding: getWidthPx(40)),
29 | Expanded(
30 | child: WebView(
31 | initialUrl: url,
32 | javascriptMode: JavascriptMode.unrestricted,
33 | ),
34 | ),
35 | ],
36 | ),
37 | ));
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/base_framework/utils/colors_util.dart:
--------------------------------------------------------------------------------
1 | import 'dart:ui' show Color;
2 |
3 |
4 | class ColorsUtil {
5 | /// 十六进制颜色,
6 | /// hex, 十六进制值,例如:0xffffff,
7 | /// alpha, 透明度 [0.0,1.0]
8 |
9 | static Color hexToColor(String s) {
10 | // 如果传入的十六进制颜色值不符合要求,返回默认值
11 | if (s == null || s.length != 7 ||
12 | int.tryParse(s.substring(1, 7), radix: 16) == null) {
13 | s = '#999999';
14 | }
15 |
16 | return new Color(int.parse(s.substring(1, 7), radix: 16) + 0xFF000000);
17 | }
18 | }
--------------------------------------------------------------------------------
/lib/base_framework/utils/isolate/factory/proxy_isolate.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/9/1
4 | */
5 |
6 |
7 | import 'dart:async';
8 | import 'dart:isolate';
9 |
10 |
11 |
12 |
13 | /*
14 | * sendPort发送的messages格式统一为 [key,dynamic],ps:这是list不是map !
15 | * 格式保持一致,减少if嵌套
16 | *
17 | * */
18 |
19 | const int kSendPortKey = 6633;//第二个元素则为 sendPort
20 |
21 | const int kTaskKey = 8844; // 第二个元素为task
22 |
23 | ///
24 | const String kMethodName = 'kMethodName';
25 | const String kNameArgs = 'kNameArgs';
26 |
27 |
28 |
29 | final List orders = [];
30 |
31 | void main( args,SendPort mainPort)async{
32 |
33 | ///connect with main isolate
34 | final ReceivePort receivePort = ReceivePort();
35 | final SendPort sendPort = receivePort.sendPort;
36 | receivePort.listen((message) {
37 | print('msg from main $message');
38 | if(message[0] == kTaskKey){
39 | orders.add(message[1]);
40 | }
41 |
42 | });
43 |
44 | mainPort.send([kSendPortKey,sendPort]);
45 |
46 | ///connect with child isolate
47 |
48 | final ReceivePort proxyPort = ReceivePort();
49 | final SendPort proxySendPort = proxyPort.sendPort;
50 | final Isolate isolate = await Isolate.spawnUri(new Uri(path: './worker_isolate.dart'), args, proxySendPort);
51 |
52 | SendPort? childSendPort;
53 |
54 | proxyPort.listen((message) {
55 | print('msg from child $message');
56 | if(message[0] == kSendPortKey){
57 | childSendPort = message[1];
58 | runTask(childSendPort);
59 | }
60 |
61 | });
62 |
63 |
64 | }
65 |
66 | void runTask(SendPort? port){
67 | final timer = Timer.periodic(Duration(seconds: 1), (timer) {
68 | String? methodName = orders.first;
69 | port!.send([kTaskKey,{kMethodName:methodName,
70 | kNameArgs:{'bb':'你好'}}]);
71 | orders.removeWhere((element) => element==methodName);
72 | });
73 | }
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/lib/base_framework/utils/isolate/factory/task_warpper.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/9/2
4 | */
5 |
6 |
7 | class TaskWrapper{
8 | final String? methodName;
9 | final Map? nameArgs;
10 |
11 | TaskWrapper(this.methodName, this.nameArgs);
12 | }
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/lib/base_framework/utils/isolate/factory/work_isolate_wrapper.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/9/2
4 | * 注意: 请勿使用任何dart:ui内的东西(即涉及到flutter的代码)
5 | */
6 |
7 | import 'dart:isolate';
8 |
9 | import 'package:flutter_bedrock/base_framework/utils/isolate/factory/worker_isolate.dart';
10 |
11 |
12 | class WorkIsolateWrapper {
13 | final int id;
14 | final ReceivePort proxyPort;
15 |
16 | final SendPort proxySendPort;
17 |
18 | final Isolate _isolate;
19 |
20 | WorkIsolateWrapper(this.id,this.proxyPort, this.proxySendPort, this._isolate);
21 |
22 | ///是否空闲
23 | bool _isFree = true;
24 | bool isStandBy()=> _isFree&&initSuccess;
25 | setStatus(bool status){
26 | _isFree = status;
27 | }
28 |
29 | SendPort? workSendPort;
30 | bool initSuccess = false;
31 | init() {
32 | _isolate.resume(_isolate.pauseCapability!);
33 | proxyPort.listen((message) {
34 | if (message[0] == kSendPortKey) {
35 | workSendPort = message[1];
36 | initSuccess = true;
37 | }else if(message[0] == kWorkDone){
38 | ///work done
39 | setStatus(true);
40 | print('isolate $id 完成了 :${message[1].toString()}');
41 | }
42 | });
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/lib/base_framework/utils/little_util.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/9
4 | */
5 |
6 |
7 | import 'dart:async';
8 |
9 | class LittleUtil{
10 |
11 | ///简单的循环执行一个task
12 | /// * 务必在不用的时候释放掉。
13 | static StreamSubscription cycleUtil(Function task,{Duration period = const Duration(seconds: 5)}){
14 | assert(task != null,"task must not null");
15 | Stream clock = Stream.periodic(period);
16 | StreamSubscription streamSubscription = clock.listen((value) {
17 | task();
18 | });
19 |
20 | return streamSubscription;
21 |
22 | }
23 |
24 |
25 | }
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/lib/base_framework/utils/platform_utils.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | import 'package:package_info_plus/package_info_plus.dart';
7 |
8 | const bool inProduction = const bool.fromEnvironment("dart.vm.product");
9 |
10 | class PlatformUtils {
11 | /// * 获取包信息
12 | static Future getAppPackageInfo() {
13 | return PackageInfo.fromPlatform();
14 | }
15 |
16 | /// * 获取版本号
17 | static Future getAppVersion() async {
18 | PackageInfo packageInfo = await PackageInfo.fromPlatform();
19 | return packageInfo.version;
20 | }
21 |
22 | /// * 获取 buildNumber
23 | static Future getBuildNum() async {
24 | PackageInfo packageInfo = await PackageInfo.fromPlatform();
25 | return packageInfo.buildNumber;
26 | }
27 |
28 |
29 |
30 | }
--------------------------------------------------------------------------------
/lib/base_framework/utils/refresh_helper.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_bedrock/base_framework/widget_state/base_stateless_widget.dart';
4 |
5 | import 'package:pull_to_refresh/pull_to_refresh.dart';
6 | import 'package:flutter_bedrock/base_framework/ui/widget/activity_indicator.dart';
7 | import 'package:flutter_bedrock/generated/l10n.dart';
8 |
9 | /// 首页列表的header
10 | class HomeRefreshHeader extends BaseStatelessWidget {
11 |
12 | final Color textColor;
13 |
14 |
15 | HomeRefreshHeader(this.textColor);
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | var strings = RefreshLocalizations.of(context)?.currentLocalization ??
20 | EnRefreshString();
21 | return ClassicHeader(
22 | canTwoLevelText: S.of(context).refreshTwoLevel,
23 | textStyle: TextStyle(color: textColor),
24 | //二楼
25 | //outerBuilder: (child) => HomeSecondFloorOuter(child),
26 | twoLevelView: Container(),
27 | height: getWidthPx(140) + MediaQuery.of(context).padding.top / 3,
28 | refreshingIcon: ActivityIndicator(brightness: Brightness.dark),
29 | releaseText: strings.canRefreshText,
30 | );
31 | }
32 | }
33 |
34 | /// 通用的footer
35 | ///
36 | /// 由于国际化需要context的原因,所以无法在[RefreshConfiguration]配置
37 | class RefresherFooter extends BaseStatelessWidget {
38 | @override
39 | Widget build(BuildContext context) {
40 | return ClassicFooter(
41 | // failedText: S.of(context).loadMoreFailed,
42 | // idleText: S.of(context).loadMoreIdle,
43 | // loadingText: S.of(context).loadMoreLoading,
44 | // noDataText: S.of(context).loadMoreNoData,
45 | );
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/lib/base_framework/utils/show_image_util.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'package:extended_image/extended_image.dart';
4 | import 'package:flutter/cupertino.dart';
5 | import 'package:flutter/material.dart';
6 |
7 | /*
8 | * 图片格式是否是webp要等于后端和IOS确定在做更改,目前是webp的
9 | *
10 | * extend_image 更多使用方法:https://github.com/fluttercandies/extended_image/blob/master/README-ZH.md
11 | *
12 | * 图片格式: webp : -w400 -w600 -w1000
13 | * */
14 |
15 |
16 |
17 | class ShowImageUtil{
18 | ///根据后台协商不同尾缀来加载不同尺寸的图片,可以换成你自己的
19 | static const String TEST = "";
20 | static const String W400 = "-w400";
21 | static const String W600 = "-w600";
22 | static const String W1000 = "-w1000";
23 |
24 |
25 | /// 获取一个图片widget
26 | /// * 支持全圆角,或者自定义圆角风格
27 | /// * 支持根据图片连接结果,显示缺省图(需释放注释)
28 | static Widget showImageWithDefaultError(String? url,double width, double height,{
29 | String imageType = TEST, double borderRadius = 0,BorderRadius? borderRStyle ,
30 | /// 缺省widget ///错误widget
31 | Widget? defaultImg, Widget? errorImg, BoxFit boxFit : BoxFit.cover}){
32 |
33 | return ClipRRect(
34 | borderRadius:borderRStyle??BorderRadius.all(Radius.circular(borderRadius)) ,
35 | child: ExtendedImage.network(
36 | "$url$imageType",
37 | width: width,
38 | height: height,
39 | fit: boxFit,
40 | borderRadius: BorderRadius.all(Radius.circular(borderRadius)),
41 | cache: true,
42 | //不同状态加载不同图片
43 | // loadStateChanged: (ExtendedImageState state){
44 | // switch(state.extendedImageLoadState){
45 | // case LoadState.loading:
46 | // return Container();
47 | // case LoadState.completed:
48 | //
49 | // return ExtendedRawImage(
50 | // image: state.extendedImageInfo?.image,
51 | // width: width,
52 | // height: height,fit: boxFit,);
53 | // case LoadState.failed:
54 | // // TODO: Handle this case.
55 | // return Container();
56 | // default :
57 | // return Container();
58 | // }
59 | // },
60 | ),
61 | );
62 | }
63 |
64 |
65 |
66 |
67 |
68 |
69 | }
--------------------------------------------------------------------------------
/lib/base_framework/view_model/app_model/app_cache_model.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/4/8
4 | */
5 |
6 |
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_bedrock/base_framework/utils/platform_utils.dart';
10 | import 'package:package_info_plus/package_info_plus.dart';
11 |
12 |
13 |
14 | /// 应用缓存
15 |
16 | class AppCacheModel extends ChangeNotifier{
17 |
18 | //以下只是DEMO 演示,
19 | late PackageInfo packageInfo;
20 | late String appVersion;
21 | String? buildNum;
22 |
23 | AppCacheModel(){
24 | ///这里只是demo使用
25 | initAppInfo();
26 | }
27 |
28 |
29 | initAppInfo()async{
30 | packageInfo = await PlatformUtils.getAppPackageInfo();
31 | appVersion = await PlatformUtils.getAppVersion();
32 | buildNum = await PlatformUtils.getBuildNum();
33 | }
34 |
35 |
36 | }
--------------------------------------------------------------------------------
/lib/base_framework/view_model/app_model/app_status_model.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/8/28
4 | */
5 |
6 |
7 |
8 | enum NetStatus{
9 | Enable,Disable
10 | }
11 |
12 | enum NetType{
13 | wifi,mobile,none
14 | }
15 | /*
16 | * 如果这个类不适合你的需求,可以根据自己的需求更改。
17 | * 仅做参考
18 | * */
19 | class AppStatusModel {
20 | static AppStatusModel? _singleton;
21 | factory AppStatusModel() => _getInstance()!;
22 | static AppStatusModel? _getInstance(){
23 | if(_singleton == null){
24 | _singleton = AppStatusModel._();
25 | }
26 | return _singleton;
27 | }
28 |
29 | AppStatusModel._();
30 |
31 |
32 | ///网络是否可用
33 | NetStatus netStatus = NetStatus.Enable;
34 | setNetStatus(NetStatus status){
35 | netStatus = status;
36 | }
37 |
38 | ///网络连接方式
39 |
40 | NetType? netType ;
41 | setNetType(NetType type){
42 | netType = type;
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 |
--------------------------------------------------------------------------------
/lib/base_framework/view_model/app_model/device_model.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import 'dart:io';
5 |
6 |
7 | import 'package:device_info_plus/device_info_plus.dart';
8 | import 'package:flustars/flustars.dart';
9 | import 'package:flutter/cupertino.dart';
10 | import 'package:flutter_bedrock/base_framework/config/frame_constant.dart';
11 |
12 |
13 | class DeviceModel extends ChangeNotifier{
14 |
15 | DeviceModel({this.isAndroid,this.isIOS,this.androidDeviceInfo,this
16 | .iosDeviceInfo}){
17 | assembleDeviceInfo();
18 | }
19 | assembleDeviceInfo()async{
20 | DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
21 | if(Platform.isAndroid){
22 | //load android device info
23 | await deviceInfoPlugin.androidInfo.then((deviceInfo){
24 | isAndroid = true;
25 | androidDeviceInfo = deviceInfo;
26 | SpUtil.putString(BaseFrameConstant.DEVICE_UUID, deviceInfo.androidId ?? '');
27 | });
28 |
29 | }else{
30 | //load ios device info
31 | await deviceInfoPlugin.iosInfo.then((deviceInfo){
32 | isIOS = true;
33 | iosDeviceInfo = deviceInfo;
34 | SpUtil.putString(BaseFrameConstant.DEVICE_UUID, deviceInfo.identifierForVendor ?? '');
35 |
36 | });
37 |
38 | }
39 | }
40 |
41 | bool? isAndroid = false;
42 | bool? isIOS = false;
43 |
44 | AndroidDeviceInfo? androidDeviceInfo;
45 | IosDeviceInfo? iosDeviceInfo;
46 |
47 |
48 | }
--------------------------------------------------------------------------------
/lib/base_framework/view_model/app_model/locale_model.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'package:flustars/flustars.dart';
4 | import 'package:flutter_bedrock/generated/l10n.dart';
5 |
6 |
7 |
8 |
9 | class LocaleModel extends ChangeNotifier {
10 | // static const localeNameList = ['auto', '中文', 'English'];
11 | ///Platform.localeName
12 | ///有些手机 简体中文是 zh_Hans_CN 繁体是 zh_Hant_TW
13 | ///有些手机 中文简体是 zh_CN 繁体是 zh_TW
14 | ///台湾标识建议使用tw-CH,zh_TW可能引起问题
15 | //static const localeValueList = ['', 'zh-CH', 'en-US',"tw-CH"];
16 | static const localeValueList = ['', 'zh-CH', 'en-US'];
17 |
18 | //
19 | static const kLocaleIndex = 'kLocaleIndex';
20 |
21 | int? _localeIndex = 1;
22 |
23 | int? get localeIndex => _localeIndex;
24 |
25 | Locale? get locale {
26 | //初始化放在全局, 放在下面会导致每次刷新index 并且导致国际化切换失败
27 | //_localeIndex = 1;
28 | if (_localeIndex! > 0) {
29 | var value = localeValueList[_localeIndex!].split("-");
30 | return Locale(value[0], value.length == 2 ? value[1] : '');
31 | }
32 | // 跟随系统
33 | return null;
34 | }
35 |
36 | LocaleModel() {
37 | _localeIndex = SpUtil.getInt(kLocaleIndex) == 0 ? 2 : SpUtil.getInt(kLocaleIndex);
38 | }
39 |
40 | switchLocale(int index) {
41 | _localeIndex = index;
42 | notifyListeners();
43 | SpUtil.putInt(kLocaleIndex, index);
44 | }
45 |
46 | static String localeName(index, context) {
47 | switch (index) {
48 | case 0:
49 | return S.of(context).autoBySystem;
50 | case 1:
51 | return '简体中文';
52 | case 2:
53 | return 'English';
54 | default:
55 | return '';
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/lib/base_framework/view_model/app_model/user_view_model.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/3/10
4 | */
5 |
6 |
7 | import 'package:flustars/flustars.dart';
8 | import 'package:flutter/cupertino.dart';
9 | import 'package:flutter_bedrock/page/mine/entity/user_entity.dart';
10 |
11 |
12 |
13 |
14 | class UserViewModel extends ChangeNotifier{
15 | //此字段保存上一个登录的用户ID,之后根据此ID去取用户缓存
16 | //默认自动登录上一个用户
17 | static const String last_user_id = "lastUserId";
18 |
19 |
20 |
21 |
22 | UserEntity? _userEntity;
23 |
24 | get user => _userEntity;
25 |
26 | String get getUserId => _userEntity!.id.toString();
27 | String? get getUserName => _userEntity!.nickName;
28 |
29 |
30 |
31 | bool get hasUser => _userEntity != null;
32 |
33 |
34 |
35 | String? get userId => _userEntity == null ? "": _userEntity!.id;
36 |
37 | // 是否显示 动态轮播图
38 |
39 |
40 | UserViewModel(){
41 | ///用户登陆后会在本地缓存用户实体,可以根据自己的实际需求改变
42 | var entity = SpUtil.getObject(last_user_id);
43 | if(entity != null){
44 | _userEntity = UserEntity.fromJson(entity as Map);
45 | }else{
46 | _userEntity = null;
47 | }
48 | }
49 |
50 | updateUserModelInfo(){
51 | notifyListeners();
52 | }
53 |
54 |
55 | saveUser(UserEntity userEntity){
56 | if(userEntity == null){
57 | return ;
58 | }
59 | _userEntity = userEntity;
60 | notifyListeners();
61 | SpUtil.putObject(last_user_id, userEntity);
62 |
63 | }
64 |
65 |
66 |
67 | ///登出后,清除缓存,不再自动登录
68 | userLogout(){
69 | _userEntity = null;
70 | notifyListeners();
71 | SpUtil.remove(last_user_id);
72 | }
73 |
74 | }
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/lib/base_framework/view_model/custome_view_state_model.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/8
4 | */
5 |
6 |
7 | import 'package:flutter_bedrock/base_framework/view_model/view_state_model.dart';
8 |
9 |
10 | ///这是一个干净的viewModel 啥都没有,继承后自己扩展。
11 | ///注意对接口进行异常捕捉
12 |
13 | abstract class CustomViewStateModel extends ViewStateModel{
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 |
--------------------------------------------------------------------------------
/lib/base_framework/view_model/handle/exception_handler.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/16
4 | */
5 |
6 |
7 | import 'package:dio/dio.dart';
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_bedrock/base_framework/exception/un_authorized_exception.dart';
10 | import 'package:flutter_bedrock/base_framework/exception/user_unbind_exception.dart';
11 | import 'package:flutter_bedrock/base_framework/view_model/view_state_model.dart';
12 |
13 | class ExceptionHandler{
14 |
15 | static ExceptionHandler? _singleton;
16 |
17 | static ExceptionHandler? getInstance(){
18 | if(_singleton == null){
19 | _singleton = ExceptionHandler._();
20 | }
21 | return _singleton;
22 | }
23 |
24 | ExceptionHandler._();
25 |
26 |
27 | /// Handle Error and Exception
28 | ///
29 | /// 统一处理子类的异常情况
30 | /// [e],有可能是Error,也有可能是Exception.所以需要判断处理
31 | /// [s] 为堆栈信息
32 | void handleException(T model,e,s){
33 | debugPrint("e : $e");
34 | debugPrint("s : $s");
35 | if(e is DioError){
36 | if(e.error is UnAuthorizedException){
37 | model.setUnAuthorized();
38 | }
39 | if(e.error is UserUnbindException){
40 | model.setUnBind();
41 | }
42 | if(e.type == DioErrorType.connectTimeout ){
43 | //todo
44 | }
45 | ///以下是demo 代码,实际项目最好删除掉
46 | assert((){
47 | ///我没有服务器,为了测试未登录下请求接口,并捕获抛出的未登录异常,
48 | ///这里将dio抛出的SocketException(因为使用了www.baidu.com当做服务地址,所以请求接口时会抛出这个异常)
49 | ///当做咱们抛出的未登录异常,并对他处理
50 | if(e.error is Exception){
51 | ///这信息只是我临时设置用于DEMO,最好从异常里面取(也就是后端的message),当然也可以自己写。
52 | String temp = '用户未登录';
53 | model.setUnAuthorized(toast: temp);
54 | }
55 | return true;
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 |
--------------------------------------------------------------------------------
/lib/base_framework/view_model/interface/cache_data_factory.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/15
4 | */
5 |
6 | import 'package:flutter_bedrock/base_framework/utils/mmkv_flutter.dart';
7 | import 'package:flutter_bedrock/base_framework/view_model/view_state_model.dart';
8 | import 'package:oktoast/oktoast.dart';
9 |
10 |
11 | ///页面数据缓存,用于在无网的情况下显示缓存数据
12 | /// * 如果你的数据实体没有基类,那么泛型[T] 只能是String。
13 | /// * 如果有基类,例如
14 | /// ```dart
15 | /// abstract class BaseEntity{
16 | /// fromJson(json);
17 | /// Map toJson();
18 | ///
19 | /// }
20 | ///
21 | /// ```
22 | ///
23 | /// 那么你可以调整 为
24 | /// 之后便可以直接交付对应数据实体,而非raw data.
25 |
26 | mixin CacheDataFactory on ViewStateModel{
27 | /// 缓存单一数据
28 | /// * 一般情况下是json string
29 | T cacheData();
30 | /// 从缓存中取出数据
31 | void fetchCacheData(T cache);
32 | /// 缓存list数据
33 | /// * 一般情况下是List
34 | List cacheListData();
35 | /// 从缓存中取出数据List
36 | void fetchListCacheData(List cacheList);
37 |
38 | /// 将首次记载的数据进行缓存
39 | cacheRefreshData()async{
40 | final mmkv = await MmkvFlutter.getInstance();
41 | int i=0;
42 | for(T t in cacheListData()){
43 | await mmkv!.setString('${this.runtimeType.toString()}$i','$t');
44 | i +=1;
45 | }
46 |
47 | }
48 |
49 | /// 显示上一次缓存的数据
50 | showCacheData()async{
51 | showToast('请检查网络状态');
52 | final mmkv = await MmkvFlutter.getInstance();
53 | ///总是取第一页作为临时展示
54 | List cacheList = [];
55 | List> futures = [];
56 | for(int i=0 ; i < 10; i++){
57 | futures.add(mmkv!.getString('${this.runtimeType.toString()}$i'));
58 | }
59 | Future.wait(futures).then((value){
60 | cacheList.addAll(value);
61 | cacheList.removeWhere((element) => (element!.isEmpty || element.contains('null')));
62 | if(cacheList.isEmpty ){
63 | setEmpty();
64 | }else{
65 | cacheDataFactory!.fetchListCacheData(cacheList);
66 | setBusy(false);
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 |
--------------------------------------------------------------------------------
/lib/base_framework/view_model/list_view_state_model.dart:
--------------------------------------------------------------------------------
1 | import 'handle/exception_handler.dart';
2 | import 'view_state_model.dart';
3 |
4 | /// 基于
5 | abstract class ListViewStateModel extends ViewStateModel {
6 | /// 分页第一页页码
7 | final int pageNumFirst = 1;
8 |
9 | /// 分页条目数量
10 | final int pageSize = 10;
11 |
12 | /// 页面数据
13 | List list = [];
14 |
15 | ///第一次加载
16 | bool firstInit = true;
17 |
18 | /// 第一次进入页面loading skeleton
19 | initData() async {
20 | setBusy(true);
21 | if (cacheDataFactory != null) {
22 | ///
23 | bool netStatus = await checkNet();
24 | if (netStatus) {
25 | ///没网 的情况下
26 | await cacheDataFactory!.showCacheData();
27 | return;
28 | }
29 | }
30 | await refresh(init: true);
31 | }
32 |
33 | // 下拉刷新
34 | refresh({bool init = false}) async {
35 | //firstInit = init;
36 | try {
37 | List? data = await loadData();
38 | if (data == null || data.isEmpty) {
39 | setEmpty();
40 | } else {
41 | list = data;
42 | onCompleted(data);
43 | if (init) {
44 | firstInit = false;
45 | //改变页面状态为非加载中
46 | setBusy(false);
47 | } else {
48 | notifyListeners();
49 | }
50 | onRefreshCompleted();
51 | }
52 | } catch (e, s) {
53 | ExceptionHandler.getInstance()!.handleException(this, e, s);
54 | }
55 | }
56 |
57 | // 加载数据
58 | Future?> loadData();
59 |
60 | ///数据获取后会调用此方法,此方法在notifyListeners()之前
61 | onCompleted(List data) {}
62 |
63 | ///状态刷新后会调用此方法,此方法在notifyListeners()之后
64 | onRefreshCompleted() {}
65 | }
66 |
--------------------------------------------------------------------------------
/lib/base_framework/view_model/refresh_list_view_state_model.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/foundation.dart';
2 | import 'package:flutter_bedrock/base_framework/view_model/handle/exception_handler.dart';
3 | import 'package:pull_to_refresh/pull_to_refresh.dart';
4 |
5 | import 'list_view_state_model.dart';
6 |
7 | /// 基于
8 | abstract class RefreshListViewStateModel extends ListViewStateModel {
9 | RefreshController _refreshController =
10 | RefreshController(initialRefresh: false);
11 |
12 | RefreshController get refreshController => _refreshController;
13 |
14 | /// 当前页码
15 | int _currentPageNum = 1;
16 |
17 | get currentPageNum => pageNumFirst;
18 |
19 | ///每页加载数量
20 | get pageDataSize => pageSize;
21 |
22 | /// 下拉刷新
23 | Future?> refresh({bool init = false}) async {
24 | //firstInit = init;
25 | try {
26 | _currentPageNum = pageNumFirst;
27 | list.clear();
28 | var data = await loadData(pageNum: pageNumFirst);
29 | if (data == null || data.isEmpty) {
30 | setEmpty();
31 | } else {
32 | onCompleted(data);
33 | list.addAll(data);
34 | refreshController.refreshCompleted();
35 | if (data.length < pageSize) {
36 | refreshController.loadNoData();
37 | } else {
38 | //防止上次上拉加载更多失败,需要重置状态
39 | refreshController.loadComplete();
40 | }
41 | if (init) {
42 | firstInit = false;
43 | //改变页面状态为非加载中
44 | setBusy(false);
45 | } else {
46 | notifyListeners();
47 | }
48 | onRefreshCompleted();
49 |
50 | ///第一次加载且已注册缓存功能的,才进行缓存
51 | if (init && cacheDataFactory != null) {
52 | cacheDataFactory!.cacheRefreshData();
53 | }
54 | }
55 | return data;
56 | } catch (e, s) {
57 | ExceptionHandler.getInstance()!.handleException(this, e, s);
58 | return null;
59 | }
60 | }
61 |
62 | /// 上拉加载更多
63 | Future?> loadMore() async {
64 | try {
65 | List? data = await loadData(pageNum: ++_currentPageNum);
66 | if (data == null || data.isEmpty) {
67 | _currentPageNum--;
68 | refreshController.loadNoData();
69 | } else {
70 | list.addAll(data);
71 | onCompleted(data);
72 | if (data.length < pageSize) {
73 | refreshController.loadNoData();
74 | } else {
75 | refreshController.loadComplete();
76 | }
77 | notifyListeners();
78 | }
79 | return data;
80 | } catch (e, s) {
81 | _currentPageNum--;
82 | refreshController.loadFailed();
83 | debugPrint('error--->\n' + e.toString());
84 | debugPrint('statck--->\n' + s.toString());
85 | return null;
86 | }
87 | }
88 |
89 | // 加载数据
90 | Future?> loadData({int? pageNum});
91 |
92 | @override
93 | void dispose() {
94 | _refreshController.dispose();
95 | super.dispose();
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/lib/base_framework/view_model/single_view_state_model.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import 'package:flutter_bedrock/base_framework/view_model/view_state_model.dart';
5 |
6 | import 'handle/exception_handler.dart';
7 |
8 | abstract class SingleViewStateModel extends ViewStateModel{
9 |
10 | T? data ;
11 |
12 | initData()async{
13 | setBusy(true);
14 | await fetchData(fetch: true);
15 | }
16 |
17 | fetchData({bool fetch = false})async{
18 | try{
19 | T temp = await loadData()!;
20 | if(temp == null){
21 | setEmpty();
22 | }else{
23 | data = temp;
24 | onCompleted(temp);
25 | if(fetch){
26 | setBusy(false);
27 | }else{
28 | notifyListeners();
29 | }
30 | }
31 | } catch (e,s){
32 | ExceptionHandler.getInstance()!.handleException(this, e, s);
33 | }
34 | }
35 |
36 |
37 | Future? loadData();
38 | //数据获取后会调用此方法,此方法在notifyListeners()之前
39 | ///此方法仅在页面状态变化前,对外暴露一下数据
40 | /// * e.g. 需要对接口返回的数据进行二次处理
41 | onCompleted(T data);
42 |
43 | }
--------------------------------------------------------------------------------
/lib/base_framework/view_model/view_state.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | enum ViewState {
4 | idle,
5 | busy,
6 | empty,
7 | error,
8 | unAuthorized ,
9 | unBind,
10 | noNet
11 | }
12 |
13 | //enum ConnectivityStatus { WiFi, Cellular, Offline }
14 |
15 | //enum ListViewState{idle,busy,empty,error,unAuthorized}
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/lib/base_framework/widget_state/base_stateless_widget.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'package:flustars/flustars.dart';
4 | import 'package:flutter/cupertino.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:flutter/services.dart';
7 | import 'package:flutter_bedrock/base_framework/ui/behavior/over_scroll_behavior.dart';
8 | import 'package:flutter_bedrock/base_framework/extension/size_adapter_extension.dart';
9 | export 'package:flutter_bedrock/base_framework/extension/size_adapter_extension.dart';
10 |
11 |
12 | /// Stateless widget 继承此类
13 |
14 | abstract class BaseStatelessWidget extends StatelessWidget{
15 |
16 |
17 | ///切换状态栏 模式:light or dark
18 | ///应在根位置调用此方法
19 | Widget switchStatusBar2Dark({bool isSetDark = false,required Widget child,
20 | EdgeInsets? edgeInsets}){
21 | return AnnotatedRegion(
22 | value: isSetDark ? SystemUiOverlayStyle.dark : SystemUiOverlayStyle.light,
23 | child: Material(
24 | child: Padding(
25 | padding: edgeInsets??EdgeInsets.only(bottom: ScreenUtil.getInstance().bottomBarHeight),
26 | child: child,
27 | ),
28 | ),
29 | );
30 | }
31 |
32 | ///去掉 scroll view的 水印 e.g : listView scrollView
33 | ///
34 | Widget getNoInkWellListView({required Widget scrollView}){
35 | return ScrollConfiguration(
36 | behavior: OverScrollBehavior(),
37 | child: scrollView,
38 | );
39 | }
40 |
41 |
42 | ///占位widget
43 | Widget getSizeBox({double width = 1,double height = 1}){
44 | return SizedBox(
45 | width: width,
46 | height: height,
47 | );
48 | }
49 |
50 |
51 |
52 | /*
53 | * size adapter with tool ScreenUtil
54 | *
55 | * */
56 |
57 | double getHeightPx(double height) => ScreenUtil.getInstance().getHeightPx
58 | (height);
59 |
60 | double getWidthPx(double width) => ScreenUtil.getInstance().getWidthPx(width);
61 |
62 | ///屏幕宽度
63 | double getScreenWidth()=>ScreenUtil.getInstance().screenWidth;
64 | ///屏幕高度
65 | double getScreenHeight()=>ScreenUtil.getInstance().screenHeight;
66 |
67 | //目前仅对于手机: 因为手机大多数情况下是长度变化较大,
68 | // 所以以高度来算出半径,保证异形屏的弧度不会缩小
69 | ///有其他需求,还需要重改
70 | double getRadiusFromHeight(double raidus) => ScreenUtil.getInstance().getHeightPx(raidus);
71 |
72 | double getSp(double fontSize) => ScreenUtil.getInstance().getSp(fontSize);
73 |
74 | }
--------------------------------------------------------------------------------
/lib/base_framework/widget_state/binding/manipulate_widget_binding.dart:
--------------------------------------------------------------------------------
1 |
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_bedrock/base_framework/ui/widget/float_container_widget.dart';
4 | import 'package:flutter_bedrock/base_framework/widget_state/base_state.dart';
5 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
6 | import 'package:flutter_bedrock/base_framework/widget_state/widget_state.dart';
7 |
8 |
9 | /// 对widget的操作类
10 | /// * [PageState] 或者 [WidgetState] 相关widget的操控放入此类内
11 | /// * 一般混入[BaseState]
12 |
13 | mixin ManipulateWidgetBinding on State {
14 | ///弹出自定义widget(效果类似dialog)
15 | ///你可以调整你的widget来达到预期的表现效果
16 | ///也可以通过PageRouteBuilder的参数进行调整
17 | void floatWidget(Widget child,{
18 | //弹出层的退出由此参数控制
19 | //默认值Navigator.pop(ctx),或自定义
20 | FloatWidgetDismiss? floatWidgetDismiss,
21 | bool barrierDismissible = false,
22 | //浮层背景色
23 | Color bgColor = const Color.fromRGBO(34, 34, 34, 0.3),
24 | //浮层对齐方式
25 | Alignment alignment = Alignment.center,
26 | //回调
27 | Function? afterPop,Function? onComplete,
28 | //页面进入/退出时间
29 | Duration transitionDuration = const Duration(milliseconds: 0),
30 | //新版本 此参数已作废
31 | @deprecated
32 | Duration reverseTransitionDuration = const Duration(milliseconds: 0),
33 | }){
34 | Navigator.of(context).push(
35 | PageRouteBuilder(
36 | settings: RouteSettings(name: floatLayerRouteName),
37 | transitionDuration: transitionDuration,
38 | //新版本无此参数
39 | //reverseTransitionDuration: reverseTransitionDuration,
40 | opaque: false,
41 | pageBuilder:(ctx,animation,secondAnimation){
42 | return FloatContainerWidget(child,
43 | floatWidgetDismiss: floatWidgetDismiss??(ctx)=>Navigator.pop(ctx),
44 | barrierDismissible:barrierDismissible ,bgColor: bgColor,alignment: alignment).transformToPageWidget();
45 | }))
46 | .then((value) => afterPop??(){})
47 | .whenComplete(() => onComplete??(){});
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 |
--------------------------------------------------------------------------------
/lib/base_framework/widget_state/widget_state.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/9/8
4 | */
5 |
6 | import 'package:flutter_bedrock/base_framework/widget_state/base_state.dart';
7 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
8 | import 'package:flutter_bedrock/base_framework/extension/size_adapter_extension.dart';
9 | export 'package:flutter_bedrock/base_framework/extension/size_adapter_extension.dart';
10 |
11 | /// * 如果是页面,继承 [PageState]
12 | /// * 如果是view,继承 [WidgetState]
13 | ///
14 | /// 此处扩展功能 应该只与 view相关
15 |
16 | abstract class WidgetState extends BaseState with WidgetGenerator {
17 | ///刷新widget sate
18 | refreshState({Function? task}) {
19 | if (mounted) {
20 | setState(task == null ? () {} : task());
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/lib/generated/intl/messages_all.dart:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
2 | // This is a library that looks up messages for specific locales by
3 | // delegating to the appropriate library.
4 |
5 | // Ignore issues from commonly used lints in this file.
6 | // ignore_for_file:implementation_imports, file_names, unnecessary_new
7 | // ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering
8 | // ignore_for_file:argument_type_not_assignable, invalid_assignment
9 | // ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases
10 | // ignore_for_file:comment_references
11 |
12 | import 'dart:async';
13 |
14 | import 'package:intl/intl.dart';
15 | import 'package:intl/message_lookup_by_library.dart';
16 | import 'package:intl/src/intl_helpers.dart';
17 |
18 | import 'messages_en.dart' as messages_en;
19 | import 'messages_zh.dart' as messages_zh;
20 |
21 | typedef Future LibraryLoader();
22 | Map _deferredLibraries = {
23 | 'en': () => new Future.value(null),
24 | 'zh': () => new Future.value(null),
25 | };
26 |
27 | MessageLookupByLibrary? _findExact(String localeName) {
28 | switch (localeName) {
29 | case 'en':
30 | return messages_en.messages;
31 | case 'zh':
32 | return messages_zh.messages;
33 | default:
34 | return null;
35 | }
36 | }
37 |
38 | /// User programs should call this before using [localeName] for messages.
39 | Future initializeMessages(String localeName) async {
40 | var availableLocale = Intl.verifiedLocale(
41 | localeName, (locale) => _deferredLibraries[locale] != null,
42 | onFailure: (_) => null);
43 | if (availableLocale == null) {
44 | return new Future.value(false);
45 | }
46 | var lib = _deferredLibraries[availableLocale];
47 | await (lib == null ? new Future.value(false) : lib());
48 | initializeInternalMessageLookup(() => new CompositeMessageLookup());
49 | messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);
50 | return new Future.value(true);
51 | }
52 |
53 | bool _messagesExistFor(String locale) {
54 | try {
55 | return _findExact(locale) != null;
56 | } catch (e) {
57 | return false;
58 | }
59 | }
60 |
61 | MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {
62 | var actualLocale =
63 | Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null);
64 | if (actualLocale == null) return null;
65 | return _findExact(actualLocale);
66 | }
67 |
--------------------------------------------------------------------------------
/lib/generated/intl/messages_en.dart:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
2 | // This is a library that provides messages for a en locale. All the
3 | // messages from the main program should be duplicated here with the same
4 | // function name.
5 |
6 | // Ignore issues from commonly used lints in this file.
7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes
11 |
12 | import 'package:intl/intl.dart';
13 | import 'package:intl/message_lookup_by_library.dart';
14 |
15 | final messages = new MessageLookup();
16 |
17 | typedef String MessageIfAbsent(String messageStr, List args);
18 |
19 | class MessageLookup extends MessageLookupByLibrary {
20 | String get localeName => 'en';
21 |
22 | final messages = _notInlinedMessages(_notInlinedMessages);
23 | static Map _notInlinedMessages(_) => {
24 | "autoBySystem": MessageLookupByLibrary.simpleMessage("AutoBySystem"),
25 | "confirm": MessageLookupByLibrary.simpleMessage("Confirm"),
26 | "hello": MessageLookupByLibrary.simpleMessage("Hello"),
27 | "refreshTwoLevel":
28 | MessageLookupByLibrary.simpleMessage("refreshTwoLevel"),
29 | "reset": MessageLookupByLibrary.simpleMessage("Reset")
30 | };
31 | }
32 |
--------------------------------------------------------------------------------
/lib/generated/intl/messages_zh.dart:
--------------------------------------------------------------------------------
1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
2 | // This is a library that provides messages for a zh locale. All the
3 | // messages from the main program should be duplicated here with the same
4 | // function name.
5 |
6 | // Ignore issues from commonly used lints in this file.
7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes
11 |
12 | import 'package:intl/intl.dart';
13 | import 'package:intl/message_lookup_by_library.dart';
14 |
15 | final messages = new MessageLookup();
16 |
17 | typedef String MessageIfAbsent(String messageStr, List args);
18 |
19 | class MessageLookup extends MessageLookupByLibrary {
20 | String get localeName => 'zh';
21 |
22 | final messages = _notInlinedMessages(_notInlinedMessages);
23 | static Map _notInlinedMessages(_) => {
24 | "autoBySystem": MessageLookupByLibrary.simpleMessage("跟随系统"),
25 | "confirm": MessageLookupByLibrary.simpleMessage("确认"),
26 | "hello": MessageLookupByLibrary.simpleMessage("你好"),
27 | "refreshTwoLevel": MessageLookupByLibrary.simpleMessage("去二楼"),
28 | "reset": MessageLookupByLibrary.simpleMessage("重置")
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/lib/l10n/intl_en.arb:
--------------------------------------------------------------------------------
1 | {
2 | "hello": "Hello",
3 | "autoBySystem": "AutoBySystem",
4 | "confirm": "Confirm",
5 | "reset": "Reset",
6 | "refreshTwoLevel":"refreshTwoLevel"
7 | }
--------------------------------------------------------------------------------
/lib/l10n/intl_zh.arb:
--------------------------------------------------------------------------------
1 | {
2 | "hello": "你好",
3 | "autoBySystem": "跟随系统",
4 | "confirm": "确认",
5 | "reset": "重置",
6 | "refreshTwoLevel":"去二楼"
7 | }
--------------------------------------------------------------------------------
/lib/main.reflectable.dart:
--------------------------------------------------------------------------------
1 | // This file has been generated by the reflectable package.
2 | // https://github.com/dart-lang/reflectable.
3 |
4 | import 'dart:core';
5 |
6 | // ignore:unused_import
7 | import 'package:reflectable/mirrors.dart' as m;
8 |
9 | // ignore:unused_import
10 | import 'package:reflectable/reflectable.dart' as r show Reflectable;
11 |
12 | // ignore: implementation_imports
13 | import 'package:reflectable/src/reflectable_builder_based.dart' as r;
14 |
15 | import 'main.dart' as prefix0;
16 |
17 | final _data = {
18 | const prefix0.MyReflectable(): r.ReflectorData(
19 | [
20 | r.NonGenericClassMirrorImpl(
21 | r'WorkList',
22 | r'.WorkList',
23 | 7,
24 | 0,
25 | const prefix0.MyReflectable(),
26 | const [-1],
27 | null,
28 | null,
29 | -1,
30 | {},
31 | {},
32 | {r'': (b) => () => b ? prefix0.WorkList() : null},
33 | -1,
34 | -1,
35 | const [-1],
36 | null,
37 | {
38 | r'==': 1,
39 | r'toString': 0,
40 | r'noSuchMethod': 1,
41 | r'hashCode': 0,
42 | r'runtimeType': 0,
43 | r'test': 0
44 | })
45 | ],
46 | null,
47 | null,
48 | [prefix0.WorkList],
49 | 1,
50 | {
51 | r'==': (dynamic instance) => (x) => instance == x,
52 | r'toString': (dynamic instance) => instance.toString,
53 | r'noSuchMethod': (dynamic instance) => instance.noSuchMethod,
54 | r'hashCode': (dynamic instance) => instance.hashCode,
55 | r'runtimeType': (dynamic instance) => instance.runtimeType,
56 | r'test': (dynamic instance) => instance.test
57 | },
58 | {},
59 | null,
60 | [
61 | const [
62 | 0,
63 | 0,
64 | const [#n, #m]
65 | ],
66 | const [1, 0, null]
67 | ])
68 | };
69 |
70 | final dynamic _memberSymbolMap = null;
71 |
72 | void initializeReflectable() {
73 | r.data = _data;
74 | r.memberSymbolMap = _memberSymbolMap;
75 | }
76 |
--------------------------------------------------------------------------------
/lib/page/a.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/5/18
4 | */
5 |
--------------------------------------------------------------------------------
/lib/page/demo_page/exception/exception_main_page.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2021/1/8
4 | */
5 |
6 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
7 | import 'package:flutter/material.dart';
8 | import 'exception_notify_page.dart';
9 |
10 | import 'handle_exception_page.dart';
11 |
12 |
13 | class ExceptionMainPage extends PageState{
14 | @override
15 | Widget build(BuildContext context) {
16 | return switchStatusBar2Dark(child: Container(
17 | color: Colors.white,
18 | width: getWidthPx(750),height: getHeightPx(1334),
19 | padding: EdgeInsets.symmetric(horizontal: getWidthPx(40)),
20 | child: Column(
21 | mainAxisAlignment: MainAxisAlignment.center,
22 | children: [
23 |
24 | buildIntro("页面 viewModel.initData() 触发的api 业务异常"),
25 | ElevatedButton(
26 | child: Text("demo handle exception",style: TextStyle(color: Colors.black),),
27 | onPressed: (){
28 | push(HandleExceptionPageState());
29 | },
30 | ),
31 | getSizeBox(height: getHeightPx(40)),
32 | buildIntro("页面/widget 对业务异常的监听(即,非initData()所触发的业务异常)。"),
33 | ElevatedButton(
34 | child: Text("demo handle exception",style: TextStyle(color: Colors.black),),
35 | onPressed: (){
36 | push(ExceptionNotifyPage());
37 | },
38 | ),
39 |
40 | ],
41 | ),
42 | ));
43 | }
44 |
45 | Widget buildIntro(String str){
46 | return Text(str,style: TextStyle(color: Colors.black,fontSize: getSp(28)),);
47 | }
48 |
49 | }
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/lib/page/demo_page/exception/exception_notify_page.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2021/1/8
4 | */
5 |
6 |
7 | import 'package:flutter_bedrock/base_framework/config/net/base_http.dart';
8 | import 'package:flutter_bedrock/base_framework/ui/widget/provider_widget.dart';
9 | import 'package:flutter_bedrock/base_framework/view_model/single_view_state_model.dart';
10 | import 'package:flutter_bedrock/base_framework/view_model/view_state_model.dart';
11 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
12 | import 'package:flutter/material.dart';
13 |
14 |
15 | class ExceptionNotifyPage extends PageState{
16 | @override
17 | Widget build(BuildContext context) {
18 | return switchStatusBar2Dark(child:
19 | ProviderWidget(
20 | model: NotifyPageVM(),
21 | onModelReady: (model){
22 | //todo
23 | },
24 | builder: (ctx,model,child){
25 | return Container(
26 | color: Colors.white,
27 | width: getWidthPx(750),height: getHeightPx(1334),
28 | child: Column(
29 | mainAxisAlignment: MainAxisAlignment.center,
30 | children: [
31 | Text('没有现成的接口,请看一下这个页面的代码注释以了解使用方法吧。',
32 | style: TextStyle(color: Colors.black,fontSize: getSp(32)),),
33 | ],
34 | ),
35 | );
36 | },
37 | ));
38 | }
39 |
40 | }
41 |
42 | /// test page
43 |
44 | /// 此为[ExceptionNotifyPage] 页面的vm
45 |
46 | class NotifyPageVM extends SingleViewStateModel
47 | with ExceptionBinding{
48 |
49 |
50 | /// 在vm内单独使用接口并需要监听异常时(非[loadData]内),需要混入[ExceptionBinding]
51 | /// 调用[bindToExceptionHandler(listener)],随后实现[notifyException]
52 | NotifyPageVM(){
53 | /// 注册监听器
54 | bindToExceptionHandler(this);
55 | }
56 |
57 |
58 | ///我们通过model.initData()进行页面主数据的请求。
59 | ///父类会对此处的业务异常进行捕捉
60 | @override
61 | Future? loadData() {
62 |
63 | ///调用页面 数据接口
64 | return null;
65 | }
66 |
67 | @override
68 | onCompleted(data) {
69 |
70 | }
71 |
72 | ///此 ViewModel内的所有 api业务异常都会通知到下面这个方法
73 | @override
74 | void notifyException({Exception? exception, BaseResponseData? rawData}) {
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/exception/exception_view_model.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/5/29
4 | */
5 |
6 | import 'package:flutter_bedrock/base_framework/view_model/single_view_state_model.dart';
7 | import 'package:flutter_bedrock/service_api/bedrock_repository_proxy.dart';
8 |
9 | class ExceptionViewModel extends SingleViewStateModel{
10 |
11 | final String explain = """
12 | Debug模式下,虽然我们抛出的异常成功捕捉,但是依然可以看到log里还有未捕捉到的异常,如:\n
13 | throw assureDioError(_e ?? err, requestOptions);
14 | 这个过程中,程序是正常运行的。\n据dio issue上来看,这个应该是flutter的bug,因为在release模式下是可以正常捕捉的。\n
15 | 见:https://github.com/flutterchina/dio/issues/655
16 | """;
17 |
18 |
19 | @override
20 | Future loadData() {
21 | return BedrockRepositoryProxy.getInstance()!.getSectionOne().getTest();
22 | }
23 |
24 | @override
25 | onCompleted(data) {
26 |
27 | }
28 |
29 | }
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/lib/page/demo_page/exception/handle_exception_page.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/5/29
4 | */
5 |
6 |
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_bedrock/base_framework/ui/widget/progress_widget.dart';
10 | import 'package:flutter_bedrock/base_framework/ui/widget/provider_widget.dart';
11 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
12 | import 'package:flutter_bedrock/page/demo_page/exception/exception_view_model.dart';
13 |
14 |
15 |
16 | class HandleExceptionPageState extends PageState {
17 | @override
18 | Widget build(BuildContext context) {
19 |
20 | return switchStatusBar2Dark(
21 | child: ProviderWidget(
22 | model: ExceptionViewModel(),
23 | onModelReady: (model){
24 | },
25 | builder: (ctx,model,child){
26 | if(model.unAuthorized){
27 | return Container(
28 | width: getWidthPx(750),
29 | height: getHeightPx(1334),
30 | child: Column(
31 | mainAxisAlignment: MainAxisAlignment.center,
32 | children: [
33 | Text("需要登录",style: TextStyle(color: Colors.black,fontSize: getSp(36),
34 | fontWeight: FontWeight.bold),),
35 | getSizeBox(height: getWidthPx(40)),
36 | Divider(
37 | height: getWidthPx(5),
38 | color: Colors.red,
39 | ),
40 | getSizeBox(height: getWidthPx(40)),
41 | Text(model.explain,style: TextStyle(color: Colors.black,fontSize: getSp(30)),),
42 | ],
43 | ),
44 | );
45 | }
46 |
47 | if(model.busy){
48 | return Center(
49 | child: CircleProgressWidget(),
50 | );
51 | }
52 |
53 | return Container(
54 | width: getWidthPx(750),height: getHeightPx(1334),
55 | child: Column(
56 | mainAxisAlignment: MainAxisAlignment.center,
57 | children: [
58 | Text("测试未登录异常捕捉(不仅限于业务异常,其它异常如超时、无网络等都可以用类似的方法处理\n"
59 | "见 ViewStateModel : handleException()",
60 | style: TextStyle(color: Colors.black),),
61 |
62 | getSizeBox(height: getWidthPx(60)),
63 | ElevatedButton(
64 | child: Text("click request api"),
65 | onPressed: (){
66 | model.initData();
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/exception/unknow_page.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/4
4 | */
5 |
6 |
7 | import 'package:flutter/cupertino.dart';
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
10 |
11 |
12 |
13 | class UnKnowPageState extends PageState {
14 |
15 | String introduce = "路由未找到会进入此页面。\n "
16 | "理论上很难出现,但是对于一些路由路径后端控制,进行动态内部页面跳转的时候,还是有几率出现的,在此设置有一个页面,用户体验好一些。";
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return switchStatusBar2Dark(child: Container(
21 | color: Colors.white,
22 | width: getWidthPx(750),height: getHeightPx(1334),
23 | child: Column(
24 | mainAxisAlignment: MainAxisAlignment.center,
25 | children: [
26 | Text(introduce,textAlign: TextAlign.center,style: TextStyle(color: Colors.black,fontSize: getSp(28)),),
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/image/image_main_demo.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2021/1/19
4 | */
5 |
6 |
7 | import 'package:flutter/material.dart';
8 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
9 | import 'package:flutter_bedrock/page/demo_page/image/nine_image_page.dart';
10 | import 'package:flutter_bedrock/page/demo_page/image/pick_image_page.dart';
11 | class ImageMainDemo extends PageState{
12 | @override
13 | Widget build(BuildContext context) {
14 |
15 | return switchStatusBar2Dark(child: Container(
16 | width: getWidthPx(750),height: getHeightPx(1334),
17 | color: Colors.white,
18 | child: SingleChildScrollView(
19 | child: Column(
20 | mainAxisAlignment: MainAxisAlignment.center,
21 | children: [
22 | getSizeBox(height: getWidthPx(100)),
23 |
24 | getSizeBox(height: getHeightPx(40)),
25 | buildIntro("选择一个图片"),
26 | ElevatedButton(
27 | child: Text("弹出自定义的widget 演示",style: TextStyle(color: Colors.black),),
28 | onPressed: (){
29 | push(PickImagePageState());
30 | },
31 | ),
32 |
33 | getSizeBox(height: getHeightPx(40)),
34 | buildIntro("选择图片九宫格"),
35 | ElevatedButton(
36 | child: Text("九宫图演示demo",style: TextStyle(color: Colors.black),),
37 | onPressed: (){
38 | push(NineImagePage());
39 | },
40 | ),
41 |
42 | getSizeBox(height: getHeightPx(40)),
43 | ],
44 | ),
45 | ),
46 | ));
47 | }
48 |
49 | Widget buildIntro(String str){
50 | return Text(str,style: TextStyle(color: Colors.black,fontSize: getSp(28)),);
51 | }
52 |
53 | }
--------------------------------------------------------------------------------
/lib/page/demo_page/image/nine_image/nine_flow_delegate.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import 'package:flutter/material.dart';
5 |
6 | class NineFlowDelegate extends MultiChildLayoutDelegate{
7 |
8 | final List children;
9 |
10 | NineFlowDelegate(this.children);
11 |
12 | final int gap = 20;
13 |
14 | @override
15 | void performLayout(Size size) {
16 | final double itemWidth = size.width/3-30;
17 | debugPrint("$size-----$itemWidth");
18 | for(int i = 0; i< children.length ; i++){
19 | final BoxConstraints constraints = BoxConstraints(
20 | minWidth: 0.0,
21 | maxWidth: itemWidth, // The leading widget shouldn't take up more than 1/3 of the space.
22 | minHeight: itemWidth, // The height should be exactly the height of the bar.
23 | maxHeight: itemWidth,
24 | );
25 | /// 行内 child的X位置 系数
26 | final rowX = i%3;
27 | final LayoutId child = children[i];
28 |
29 | layoutChild(child.id,constraints);
30 | switch(i){
31 | case 0:
32 | case 1:
33 | case 2:
34 | positionChild(child.id, Offset((rowX*itemWidth)+gap,0));
35 | break;
36 | case 3:
37 | case 4:
38 | case 5:
39 | positionChild(child.id, Offset((rowX*itemWidth)+gap,(1*itemWidth)+gap));
40 | break;
41 | case 6:
42 | case 7:
43 | case 8:
44 | positionChild(child.id, Offset((rowX*itemWidth)+gap,(2*itemWidth)+gap));
45 | break;
46 | }
47 | }
48 | }
49 |
50 | @override
51 | bool shouldRelayout(NineFlowDelegate oldDelegate) {
52 | return oldDelegate.children.length != children.length;
53 | }
54 |
55 |
56 | }
--------------------------------------------------------------------------------
/lib/page/demo_page/image/nine_image/nine_image_vm.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2021/1/19
4 | */
5 |
6 | import 'dart:typed_data';
7 |
8 | import 'package:flutter_bedrock/base_framework/view_model/single_view_state_model.dart';
9 | import 'package:multi_image_picker/multi_image_picker.dart';
10 |
11 | class ImageDataWrapper {
12 | final Asset asset;
13 | final ByteData? byteData;
14 |
15 | const ImageDataWrapper(this.asset, this.byteData);
16 | }
17 |
18 | abstract class ImageWidgetChangeListener {
19 | void addChildWidget(ImageDataWrapper dataWrapper);
20 |
21 | void addChildrenWidget(List datas);
22 |
23 | void removeChildWidget(ImageDataWrapper dataWrapper);
24 | }
25 |
26 | class NineImageVM extends SingleViewStateModel {
27 | final List imageList = [];
28 | final ImageWidgetChangeListener _changeListener;
29 |
30 | final String tag = 'edit';
31 | final ImageDataWrapper editWrapper =
32 | ImageDataWrapper(Asset('editIdentifier', 'edit', 1, 1), null);
33 |
34 | NineImageVM(this._changeListener);
35 |
36 | void addImageData({ImageDataWrapper? data}) {
37 | imageList.add(data ?? editWrapper);
38 | _changeListener.addChildWidget(data ?? editWrapper);
39 | notifyListeners();
40 | }
41 |
42 | ///进入这个方法,最多只会是9张自选图片
43 | /// * 如果多余9张,是不会进入到这个方法
44 | void addImageList(List list) {
45 | imageList.insertAll(0, list);
46 | if (imageList.length > 9) {
47 | ///如果是9张自选图片,移除最后一张。
48 | imageList.removeLast();
49 | }
50 | _changeListener.addChildrenWidget(list);
51 | notifyListeners();
52 | //checkLength(imageList.length);
53 | }
54 |
55 | // void addImage(Asset asset){
56 | // if(imageList.length == 9){
57 | // imageList.insert(8, asset);
58 | // }else{
59 | // imageList.insert(0, asset);
60 | // }
61 | // notifyListeners();
62 | // }
63 | void deleteImage(ImageDataWrapper asset) {
64 | if (imageList.length == 9 && imageList.last.asset.name != tag) {
65 | imageList
66 | .removeWhere((element) => element.asset.name == asset.asset.name);
67 | imageList.add(editWrapper);
68 | } else {
69 | imageList
70 | .removeWhere((element) => element.asset.name == asset.asset.name);
71 | }
72 | _changeListener.removeChildWidget(asset);
73 | notifyListeners();
74 |
75 | //checkLength(imageList.length);
76 | }
77 |
78 | int row = 1;
79 | int nineImageLength = 1;
80 |
81 | void checkLength(int l) {
82 | if (l != nineImageLength) {
83 | if ((nineImageLength / 3).ceil() != (l / 3).ceil()) {
84 | nineImageLength = l;
85 | row = (nineImageLength / 3).ceil();
86 | }
87 | }
88 | notifyListeners();
89 | }
90 |
91 | void clearAllData() {
92 | imageList.clear();
93 | }
94 |
95 | @override
96 | Future? loadData() {
97 | return null;
98 | }
99 |
100 | @override
101 | onCompleted(data) {}
102 | }
103 |
--------------------------------------------------------------------------------
/lib/page/demo_page/image/nine_image_page.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2021/1/19
4 | */
5 |
6 | import 'package:flutter/material.dart';
7 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
8 | import 'package:flutter_bedrock/page/demo_page/image/nine_image/nine_image_state.dart';
9 |
10 | class NineImagePage extends PageState{
11 | @override
12 | Widget build(BuildContext context) {
13 | return switchStatusBar2Dark(child: Container(
14 | width: getWidthPx(750),height: getHeightPx(1334),
15 | child: Column(
16 | mainAxisAlignment: MainAxisAlignment.center,
17 | children: [
18 | generateWidget(() => NineImageEditorState()),
19 | //getSizeBox(height: getWidthPx(80)),
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/isolate/isolate_page.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/8/24
4 | */
5 |
6 |
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_bedrock/base_framework/config/net/bedrock_http.dart';
10 | import 'package:flutter_bedrock/base_framework/utils/isolate/app_private_ioslate.dart';
11 | import 'package:flutter_bedrock/base_framework/utils/isolate/factory/worker_isolate.dart';
12 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
13 |
14 |
15 |
16 | class IsolatePageState extends PageState {
17 |
18 | String result = '查看log';
19 |
20 | int count = 1;
21 | @override
22 | Widget build(BuildContext context) {
23 | return switchStatusBar2Dark(
24 | child: Container(
25 | width: getWidthPx(750),height: getHeightPx(1334),
26 | color: Colors.white,
27 | child: Column(
28 | mainAxisAlignment: MainAxisAlignment.center,
29 | children: [
30 | Text(result),
31 | ElevatedButton(onPressed: ()async{
32 | ///结果 在 log里
33 | AppPrivateIsolate.getInstance()!.initNetObserver();
34 |
35 | },
36 | child: Text('get work 注意日志'),),
37 | SizedBox(width: 1,height: 20,),
38 | ElevatedButton(onPressed: ()async{
39 | debugPrint('请求 google');
40 | bedRock.get('https://www.google.com');
41 |
42 | },
43 | child: Text('测试无网 取消所有连接,需要先启动上面的按钮'),),
44 | SizedBox(width: 1,height: 20,),
45 | ElevatedButton(onPressed: ()async{
46 | List.generate(100, (index){
47 | WorkerMainProxy.getInstance()!
48 | ///参数一:方法名字,参数二:方法对应的命名参数,
49 | ///务必确保参数名与WorkList中的一致
50 | .invokeWorker(methodName: 'test',nameArgs: {'n':'第$index次唤起','m':'第二个参数'});
51 |
52 | });
53 |
54 | count++;
55 |
56 | },
57 | child: Text('测试worker'),),
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/local_i10l/local_page.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_bedrock/base_framework/view_model/app_model/locale_model.dart';
6 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
7 | import 'package:flutter_bedrock/generated/l10n.dart';
8 | import 'package:provider/provider.dart';
9 |
10 |
11 | class LocalPageState extends PageState{
12 |
13 | String introduce = '''
14 | 使用介绍:\n
15 | 国际化是基于插件Intl实现的,所以在使用之前你需要安装Intl,墙裂建议百度一下Intl的详细介绍。\n
16 | 这里我简单介绍一下,在安装好Intl插件后,会在项目下生成I10n文件夹,内部应该有一个intl_en.arb,这个文件为语言资源文件
17 | (你还可以举一反三添加其他语种,需要同时在LocalModel修改),所有文本都要添加到这里,添加完成后ctrl+s保存,插件会根据资源文件内的文本生成代码,在
18 | generated文件夹下(一般情况下是不需要编辑这里面的文件的),之后通过S.of(context).你的文本 就可以访问了。\n
19 | 这个插件的好处是,前期我们可以都写成一个语种,之后交给翻译,翻译完成后在覆盖重新生成即可
20 | ''';
21 |
22 | @override
23 | Widget build(BuildContext context) {
24 | return switchStatusBar2Dark(
25 | child:Consumer(
26 | builder: (ctx,localModel,child){
27 | return Container(
28 | color: Colors.white,
29 | width: getWidthPx(750),height: getHeightPx(1334),
30 | child: Column(
31 | mainAxisAlignment: MainAxisAlignment.center,
32 | children: [
33 | Text(S.of(context).hello,style: TextStyle(color: Colors.black,fontSize: getSp(28)),),
34 | getSizeBox(height: getWidthPx(40)),
35 | Row(
36 | mainAxisAlignment: MainAxisAlignment.center,
37 | children: [
38 | ElevatedButton(
39 | onPressed: (){
40 | localModel.switchLocale(1);
41 | },
42 | child: Text("切换中文"),
43 | ),
44 | getSizeBox(width: getWidthPx(40)),
45 | ElevatedButton(
46 | onPressed: (){
47 | localModel.switchLocale(2);
48 | },
49 | child: Text("切换英文"),
50 | ),
51 | ],
52 | ),
53 | getSizeBox(height: getWidthPx(40)),
54 | Text(introduce,style: TextStyle(color: Colors.black),),
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/main/fake_constant.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/4
4 | */
5 |
6 |
7 | class FakeConstant{
8 |
9 | static const List imageList = [
10 | 'http://img.08087.cc/uploads/20190113/19/1547380421-DtxGodwgny.jpeg',
11 | 'http://img.08087.cc/uploads/20190113/19/1547380421-yuCqefakQB.jpg',
12 | 'http://img.08087.cc/uploads/20200526/21/1590499015-bRKMhvSkAg.jpg',
13 | 'http://img.08087.cc/uploads/20200526/21/1590498931-psqTFECXur.jpeg',
14 | 'http://img.08087.cc/uploads/20200526/21/1590498929-OoCIMHjEBp.jpeg',
15 | 'http://img.08087.cc/uploads/20200526/21/1590498946-jcZriBdqzh.jpg',
16 | 'http://img.08087.cc/uploads/20200526/21/1590499034-tYUnQzGfKo.jpg',
17 | 'http://img.08087.cc/uploads/20200526/21/1590498949-PXmwDVKOeG.jpeg',
18 | 'http://img.08087.cc/uploads/20200526/21/1590498949-CgekfvNcjA.jpg',
19 | 'http://img.08087.cc/uploads/20200526/21/1590498492-CaVcoyiJFd.jpg',
20 | ];
21 |
22 | }
23 |
24 |
25 |
--------------------------------------------------------------------------------
/lib/page/demo_page/main/first/entity/cache_entity.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/15
4 | */
5 |
6 |
7 | import 'dart:core';
8 |
9 | class CacheEntity{
10 | String? img;
11 | String? title;
12 |
13 | CacheEntity(this.img, this.title);
14 |
15 | CacheEntity.fromJson(Map json){
16 | this.img = json['img'];
17 | this.title = json['title'];
18 | }
19 | Map toJson(){
20 | final Map data = Map();
21 | data['img'] = this.img;
22 | data['title'] = this.title;
23 | return data;
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/main/first/entity/first_card_entity.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/1
4 | */
5 |
6 |
7 | class FirstCardEntity{
8 | String img;
9 | String title;
10 |
11 | FirstCardEntity(this.img, this.title);
12 | }
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/lib/page/demo_page/main/first/entity/first_entity.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/1
4 | */
5 |
6 | class FirstEntity{
7 | List banner = [
8 | 'https://iknow-pic.cdn.bcebos.com/6609c93d70cf3bc7dd9f82efd500baa1cd112a11?x-bce-process=image/resize,m_lfit,w_600,h_800,limit_1',
9 | 'https://iknow-pic.cdn.bcebos.com/6a63f6246b600c334c3e91cb1e4c510fd9f9a16a?x-bce-process=image/resize,m_lfit,w_600,h_800,limit_1',
10 | 'https://iknow-pic.cdn.bcebos.com/d4628535e5dde7119b460cf5a3efce1b9d166118?x-bce-process=image/resize,m_lfit,w_600,h_800,limit_1'
11 | ];
12 |
13 | List transform = [
14 | 't1','t2','t3','t4'
15 | ];
16 |
17 | }
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/lib/page/demo_page/main/first/update_page.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_bedrock/base_framework/ui/widget/provider_widget.dart';
6 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
7 | import 'package:flutter_bedrock/page/demo_page/main/first/view_model/update_view_model.dart';
8 |
9 |
10 | class UpdatePageState extends PageState {
11 |
12 | String text = '''
13 | 更新功能展示\n
14 | IOS建议直接跳app store\n
15 | 未使用flutter_downloader,可能会引起与fluwx插件的冲突,不过也不是无法解决的,这个可以自己百度
16 |
17 | ''';
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 |
22 | return switchStatusBar2Dark(
23 | child: ProviderWidget(
24 | model: UpdateViewModel(),
25 | onModelReady: (model){
26 | //todo
27 | },
28 | builder: (ctx,updateVM,child){
29 | return Container(
30 | color: Colors.white,
31 | width: getWidthPx(750),height: getHeightPx(1334),
32 | child: Column(
33 | mainAxisAlignment: MainAxisAlignment.center,
34 | children: [
35 | Text(text,),
36 | getSizeBox(height: getWidthPx(50)),
37 | ElevatedButton(
38 | child: Text('升级',style: TextStyle(color: Colors.black),),
39 | onPressed: updateVM.getNewApk,
40 | ),
41 | getSizeBox(height: getWidthPx(50)),
42 | Text('下载进度: ${updateVM.progress}/100',style: TextStyle(color: Colors.lightBlue),),
43 | getSizeBox(height: getWidthPx(50)),
44 | ElevatedButton(
45 | child: Text('取消',style: TextStyle(color: Colors.black),),
46 | onPressed: updateVM.cancelTask,
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/main/first/view_model/cache_page_vm.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/15
4 | */
5 |
6 |
7 | import 'dart:convert';
8 |
9 | import 'package:flutter_bedrock/base_framework/view_model/interface/cache_data_factory.dart';
10 | import 'package:flutter_bedrock/base_framework/view_model/refresh_list_view_state_model.dart';
11 | import 'package:flutter_bedrock/page/demo_page/main/first/entity/cache_entity.dart';
12 | import 'package:flutter_bedrock/service_api/bedrock_repository_proxy.dart';
13 |
14 | /// 缓存测试页面
15 | /// * 简单的讲,就是网络状态不佳的情况下,
16 | /// * 从本地缓存取json格式的数据,用于显示
17 | /// * 一般情况下直接缓存以string进行缓存,缓存工具是mmkv
18 | /// * 你可以在CacheDataFactory 类里更改缓存方式
19 |
20 | class CachePageVM extends RefreshListViewStateModel with CacheDataFactory {
21 |
22 | CachePageVM(){
23 | ///注册缓存功能
24 | /// * 如果不需要缓存,不要使用缓存功能
25 | this.injectCache(this);
26 | }
27 |
28 |
29 | @override
30 | Future?> loadData({int? pageNum}) {
31 | return BedrockRepositoryProxy.getInstance()!.getSectionOne().getCacheList(pageNum);
32 | }
33 |
34 | @override
35 | String cacheData() {
36 | return '';
37 | }
38 |
39 | @override
40 | List cacheListData() {
41 | List strList = [];
42 | list.forEach((element) {
43 | strList.add(jsonEncode(element));
44 | });
45 | return strList;
46 | }
47 |
48 | @override
49 | void fetchCacheData(String? cache) {
50 |
51 | }
52 | /*
53 | * 将之前缓存的list string 取出 并转为 list entity
54 | * 请务必保证json格式正确
55 | *
56 | * */
57 | @override
58 | void fetchListCacheData(List cacheList) {
59 | cacheList.forEach((element) {
60 | list.add(CacheEntity.fromJson(jsonDecode(element!)));
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/main/first/view_model/first_view_model.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/1
4 | */
5 |
6 |
7 |
8 | import 'package:flutter_bedrock/base_framework/view_model/single_view_state_model.dart';
9 | import 'package:flutter_bedrock/page/demo_page/main/first/entity/first_card_entity.dart';
10 | import 'package:flutter_bedrock/page/demo_page/main/first/entity/first_entity.dart';
11 | import 'package:flutter_bedrock/service_api/bedrock_repository_proxy.dart';
12 | import 'package:pull_to_refresh/pull_to_refresh.dart';
13 |
14 |
15 | class FirstViewModel extends SingleViewStateModel{
16 | int pageNum = 1;
17 | int pageSize = 8;
18 |
19 | late RefreshController refreshController;
20 |
21 |
22 | FirstViewModel(){
23 | refreshController = RefreshController();
24 | }
25 |
26 | FirstEntity? firstEntity;
27 | List cardList = [];
28 |
29 |
30 | @override
31 | Future loadData() {
32 | cardList.clear();
33 | List futures = [];
34 | futures.add(BedrockRepositoryProxy.getInstance()!.getSectionOne().getFirstEntity());
35 | futures.add(BedrockRepositoryProxy.getInstance()!.getSectionOne().getFirstListCard(pageNum, pageSize));
36 | var result = Future.wait(futures);
37 | return result;
38 | }
39 |
40 | refresh()async{
41 | setBusy(true);
42 | cardList.clear();
43 | pageNum = 1;
44 | BedrockRepositoryProxy.getInstance()!.getSectionOne()
45 | .getFirstListCard(pageNum, pageSize).then((list){
46 | cardList.addAll(list!);
47 | refreshController.refreshCompleted();
48 | notifyListeners();
49 | }).whenComplete((){
50 | refreshController.loadComplete();
51 | setBusy(false);
52 | });
53 | }
54 |
55 | loadMore()async{
56 | pageNum +=1;
57 | BedrockRepositoryProxy.getInstance()!.getSectionOne()
58 | .getFirstListCard(pageNum, pageSize).then((list){
59 | if(list!.isEmpty){
60 | refreshController.loadNoData();
61 | }else{
62 | cardList.addAll(list);
63 | refreshController.loadComplete();
64 | notifyListeners();
65 | }
66 | });
67 | }
68 |
69 | @override
70 | onCompleted(data) {
71 | firstEntity = data[0];
72 | cardList.addAll(data[1]);
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/main/login/login_view_model.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import 'package:flutter/cupertino.dart';
5 | import 'package:flutter_bedrock/base_framework/view_model/app_model/user_view_model.dart';
6 | import 'package:flutter_bedrock/base_framework/view_model/single_view_state_model.dart';
7 | import 'package:flutter_bedrock/service_api/bedrock_repository_proxy.dart';
8 | import 'package:oktoast/oktoast.dart';
9 |
10 | class LoginViewModel extends SingleViewStateModel{
11 |
12 | TextEditingController? nameController;
13 | TextEditingController? passController;
14 |
15 | final UserViewModel userViewModel;
16 |
17 |
18 | LoginViewModel(this.userViewModel){
19 | nameController = TextEditingController();
20 | passController = TextEditingController();
21 | }
22 |
23 | String? name;
24 | String? pass;
25 |
26 |
27 | login(){
28 | ///可以用这个,也可以自己定义
29 | setBusy(true);
30 | BedrockRepositoryProxy.getInstance()!.getSectionOne().login(name, pass)
31 | .then((user){
32 | if(user != null){
33 | showToast("登陆成功");
34 | userViewModel.saveUser(user);
35 | }
36 | })
37 | .whenComplete((){
38 | setBusy(false);
39 | });
40 |
41 | }
42 |
43 | @override
44 | Future? loadData() {
45 | ///在这里写入登录接口也可以,灵活随意
46 | return null;
47 |
48 | }
49 |
50 | @override
51 | onCompleted(data) {
52 |
53 | }
54 |
55 | }
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/lib/page/demo_page/main/second/entity/second_entity.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/4
4 | */
5 |
6 | class SecondEntity{
7 |
8 | String img;
9 | String title;
10 |
11 | SecondEntity(this.img, this.title);
12 | }
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/lib/page/demo_page/main/second/view_model/second_view_model.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/5
4 | */
5 |
6 | import 'package:flutter_bedrock/base_framework/view_model/refresh_list_view_state_model.dart';
7 | import 'package:flutter_bedrock/page/demo_page/main/second/entity/second_entity.dart';
8 | import 'package:flutter_bedrock/service_api/bedrock_repository_proxy.dart';
9 |
10 |
11 | class SecondViewModel extends RefreshListViewStateModel{
12 | @override
13 | Future?> loadData({int? pageNum}) {
14 | return BedrockRepositoryProxy.getInstance()!.getSectionOne().getSecondList(pageNum);
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/notification/notify_target_page.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | import 'package:flutter/material.dart';
6 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
7 |
8 |
9 | class NotifyTargetPage extends PageState{
10 | @override
11 | Widget build(BuildContext context) {
12 | return switchStatusBar2Dark(child: Container(
13 | width: getWidthPx(750),height: getHeightPx(1334),
14 | child: Column(
15 | mainAxisAlignment: MainAxisAlignment.center,
16 | children: [
17 | Text('点击通知所到达的页面。',style: TextStyle(fontSize: getSp(38)),)
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/other/cross_list/body_item_widget.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math';
2 |
3 | import 'package:flustars/flustars.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_bedrock/base_framework/widget_state/widget_state.dart';
6 | import 'package:flutter_bedrock/page/demo_page/other/cross_list/cross_list_vm.dart';
7 | import 'package:provider/provider.dart';
8 |
9 |
10 | class BodyItemWidget extends WidgetState{
11 |
12 | final int index;
13 |
14 | BodyItemWidget(this.index);
15 |
16 | late final CrossListVM vm;
17 |
18 | late double height;
19 |
20 | @override
21 | void initState() {
22 | super.initState();
23 | vm = Provider.of(context,listen: false);
24 | height = (Random().nextInt(20)).clamp(5, 19) * 50;
25 | }
26 |
27 |
28 | @override
29 | Widget build(BuildContext context) {
30 | vm.saveBodyItemCtx(index, context);
31 | return Container(
32 | decoration: BoxDecoration(
33 | color: Colors.white,
34 | borderRadius: BorderRadius.all(Radius.circular(getHeightPx(16))),
35 | border: Border.all(color: Colors.lightBlueAccent,width: getWidthPx(2))
36 | ),margin: EdgeInsets.only(bottom: getWidthPx(32)),
37 | child: Column(
38 | children: [
39 | Text('标题 $index',style: TextStyle(color: Colors.black,fontSize: getSp(30))),
40 | Container(
41 | width: getWidthPx(750),
42 | height: getWidthPx(height),
43 | margin: EdgeInsets.symmetric(horizontal: getWidthPx(16),vertical: getWidthPx(42)),
44 | alignment: Alignment.center,
45 | decoration: BoxDecoration(
46 | borderRadius: BorderRadius.all(Radius.circular(getHeightPx(16))),
47 | color: Colors.grey
48 | ),child: Text('内容 $index',style: TextStyle(color: Colors.white,fontSize: getSp(50))),
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/other/cross_list/tab_item_widget.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_bedrock/base_framework/widget_state/widget_state.dart';
6 | import 'package:flutter_bedrock/page/demo_page/other/cross_list/cross_list_vm.dart';
7 | import 'package:provider/provider.dart';
8 |
9 | class TabItemWidget extends WidgetState{
10 |
11 | final int tabIndex;
12 |
13 | TabItemWidget(this.tabIndex);
14 |
15 | late final CrossListVM vm;
16 |
17 | @override
18 | void initState() {
19 | // TODO: implement initState
20 | super.initState();
21 | vm = Provider.of(context,listen: false);
22 | }
23 |
24 | @override
25 | Widget build(BuildContext context) {
26 | vm.saveTabsChildCtx(tabIndex, context);
27 | return Selector(
28 | selector: (ctx, model) {
29 | return model.selectTabIndex;
30 | },
31 | builder: (ctx,value,child) {
32 | return Container(
33 | width: getWidthPx(150),
34 | padding: EdgeInsets.symmetric(horizontal: getWidthPx(20)),
35 | margin: EdgeInsets.only(right: getWidthPx(32)),
36 | decoration: BoxDecoration(
37 | color: value == tabIndex ? Colors.green : Colors.white,
38 | borderRadius: BorderRadius.all(Radius.circular(getHeightPx(16))),
39 | border: Border.all(color: Colors.black,width: getWidthPx(2))
40 | ),alignment: Alignment.center,
41 | child: Text('标题 $tabIndex',style: TextStyle(color: Colors.black,fontSize: getSp(20)),),
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/other/eye_3d.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
5 | import 'package:sensors_plus/sensors_plus.dart';
6 |
7 |
8 |
9 | const int duration = 16 * 12;
10 |
11 | class Eye3dState extends PageState {
12 |
13 | late StreamSubscription _streamSubscription;
14 | late Size size;
15 |
16 | double x = 0;
17 | double y = 0;
18 |
19 | @override
20 | void initState() {
21 | super.initState();
22 | _streamSubscription =
23 | accelerometerEvents.listen((AccelerometerEvent event) async {
24 | setState(() {
25 | x = event.x * 2.5;
26 | y = event.y * 1.8;
27 | });
28 | });
29 | }
30 |
31 | @override
32 | void dispose() {
33 | super.dispose();
34 | _streamSubscription.cancel();
35 | }
36 |
37 | @override
38 | Widget build(BuildContext context) {
39 | size = MediaQuery.of(context).size;
40 | return Material(
41 | color: Colors.white,
42 | child: Column(
43 | children: [
44 | Container(
45 | color: Colors.white,
46 | width: size.width, height: 250,
47 | child: Stack(
48 | alignment: Alignment.center,
49 | children: [
50 | AnimatedPositioned(
51 | top: y - 40,
52 | right: x - 10,
53 | duration: Duration(milliseconds: duration),
54 | child: _bottom()),
55 | Positioned(
56 | child: _middle()),
57 | AnimatedPositioned(
58 | bottom: y - 10 ,
59 | left: x + size.width / 3,
60 | duration: Duration(milliseconds: duration),
61 | child: _top()),
62 | ],
63 | ),
64 | )
65 | ],
66 | ),
67 | );
68 | }
69 |
70 | Widget _bottom() {
71 | return Container(
72 | width: size.width+60,height: 250,
73 | child: Image.asset('assets/images/bg.png',fit: BoxFit.fill,),
74 | );
75 | }
76 |
77 | Widget _middle() {
78 | return Container(
79 | width: size.width - 20,height: 80,
80 | child: Image.asset('assets/images/sds.png',fit: BoxFit.fill,),
81 | );
82 | }
83 |
84 | Widget _top() {
85 | return Container(
86 | width: 150, height: 150,
87 | child: Image.asset('assets/images/car.png',fit: BoxFit.fill),
88 | );
89 | }
90 |
91 |
92 | }
--------------------------------------------------------------------------------
/lib/page/demo_page/other/float_layer_widget/shake_float_widget.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2021/1/14
4 | */
5 |
6 | import 'package:flutter_bedrock/base_framework/widget_state/widget_state.dart';
7 |
8 | import 'package:flutter/material.dart';
9 |
10 | class ShakeFloatState extends WidgetState with SingleTickerProviderStateMixin {
11 | AnimationController? controller;
12 | late Animation animation;
13 |
14 | @override
15 | void dispose() {
16 | controller?.dispose();
17 | super.dispose();
18 | }
19 |
20 | @override
21 | void initState() {
22 | controller = AnimationController(
23 | vsync: this, duration: const Duration(milliseconds: 300));
24 | animation = Tween(begin: 0.1, end: 1.0).animate(CurvedAnimation(
25 | curve: Curves.elasticOut,
26 | reverseCurve: Curves.easeOut,
27 | parent: controller!));
28 | super.initState();
29 | controller!.addListener(() {
30 | if (!mounted) return;
31 | setState(() {});
32 | });
33 | WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
34 | controller!.forward();
35 | });
36 | }
37 |
38 | double alpha = 0.0;
39 | double width = 400;
40 |
41 | @override
42 | Widget build(BuildContext context) {
43 | return Opacity(
44 | opacity: animation.value.clamp(0.0, 1.0),
45 | child: GestureDetector(
46 | onTap: () {
47 | controller!.reverse().whenComplete(() => Navigator.of(context).pop());
48 | },
49 | child: Container(
50 | width: getWidthPx(width * animation.value),
51 | height: getWidthPx(width * animation.value),
52 | color: Colors.red,
53 | child: Icon(
54 | Icons.close,
55 | size: getWidthPx(80),
56 | color: Colors.white,
57 | ),
58 | ),
59 | ),
60 | );
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/lib/page/demo_page/other/little_util_page.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/10
4 | */
5 |
6 | import 'dart:async';
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_bedrock/base_framework/utils/little_util.dart';
10 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
11 |
12 |
13 |
14 | class LittleUtilPageState extends PageState {
15 |
16 | int count = 0;
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return switchStatusBar2Dark(child: Container(
21 | width: getWidthPx(750),height: getHeightPx(1334),
22 | child: Column(
23 | children: [
24 | getSizeBox(height: getWidthPx(100)),
25 | Divider(color: Colors.black,height: getWidthPx(4),),
26 | Text('开始自加 : $count'),
27 | ElevatedButton(
28 | onPressed: startCalculate,
29 | child: Text('cycle Util'),
30 | ),
31 | Text("我自己项目需要封装的一个小工具,如果需要复杂功能建议直接使用stream,非常强大(flutter版RXjava)"),
32 | getSizeBox(height: getWidthPx(40)),
33 | Divider(color: Colors.black,height: getWidthPx(4),),
34 | ],
35 | ),
36 | ));
37 | }
38 | StreamSubscription? streamSubscription;
39 | startCalculate()async{
40 | if(streamSubscription != null){
41 | await streamSubscription!.cancel();
42 | }
43 | streamSubscription = LittleUtil.cycleUtil((){
44 |
45 | if(mounted){
46 | count++;
47 | setState(() {
48 |
49 | });
50 | }else{
51 | ///避免内存泄漏
52 | streamSubscription!.cancel();
53 | }
54 |
55 | },period: Duration(seconds: 1));
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/other/part_refresh_page.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/11/27
4 | */
5 |
6 |
7 | import 'package:flutter/material.dart';
8 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
9 | import 'package:flutter_bedrock/base_framework/widget_state/widget_state.dart';
10 |
11 | ///仅作为一种刷新方式
12 |
13 | class PartRefreshPage extends PageState{
14 |
15 | bool exp1 = false;
16 |
17 | late PartWidget partWidget;
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | debugPrint('page build');
22 | return switchStatusBar2Dark(child: Container(
23 | child: Column(
24 | mainAxisAlignment: MainAxisAlignment.center,
25 | children: [
26 | ElevatedButton(onPressed: (){
27 | exp1 = !exp1;
28 | setState(() {
29 |
30 | });
31 | },
32 | child: Text('change color By setState()'),),
33 | Container(
34 | height: getWidthPx(100),
35 | child: Row(
36 | mainAxisAlignment: MainAxisAlignment.center,
37 | children: [
38 | Container(
39 | width: getWidthPx(80),
40 | color: exp1 ? Colors.red:Colors.blue,
41 | ),
42 | Container(
43 | width: getWidthPx(80),
44 | color: exp1 ? Colors.blue:Colors.red,
45 | ),
46 | ],
47 | ),
48 | ),
49 | getSizeBox(height: getWidthPx(100)),
50 | ElevatedButton(onPressed: (){
51 | partWidget.switchColor();
52 | }, child: Text('change color By part refresh'),),
53 | generateWidget(() {
54 | partWidget = PartWidget();
55 | return partWidget;
56 | }),
57 | ],
58 | ),
59 | ));
60 | }
61 |
62 | }
63 |
64 | class PartWidget extends WidgetState{
65 |
66 | bool exp1 = false;
67 | ///此为Demo 故,书写随意
68 | void switchColor(){
69 | if(!mounted)return;
70 | exp1 = !exp1;
71 | setState(() {
72 |
73 | });
74 | }
75 |
76 | @override
77 | Widget build(BuildContext context) {
78 | debugPrint('DoubleColorWidget build');
79 | return Container(
80 | height: getWidthPx(100),
81 | child: Row(
82 | mainAxisAlignment: MainAxisAlignment.center,
83 | children: [
84 | Container(
85 | width: getWidthPx(80),
86 | color: exp1 ? Colors.red:Colors.blue,
87 | ),
88 | Container(
89 | width: getWidthPx(80),
90 | color: exp1 ? Colors.blue:Colors.red,
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/other/request_permission_page.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
5 | import 'package:oktoast/oktoast.dart';
6 | import 'package:permission_handler/permission_handler.dart';
7 |
8 |
9 | class RequestPermissionsPageState extends PageState {
10 |
11 | String status = '权限未请求';
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return switchStatusBar2Dark(child: Container(
16 | width: getWidthPx(750), height: getHeightPx(1334),
17 | child: Column(
18 | mainAxisAlignment: MainAxisAlignment.center,
19 | children: [
20 | Text(status,style: TextStyle(color: Colors.black,fontSize: getSp(30)),),
21 | getSizeBox(height: getWidthPx(100)),
22 | ElevatedButton(
23 | child: Text('request location permission'),
24 | onPressed: (){
25 | request();
26 | },
27 | ),
28 | ],
29 | ),
30 | ));
31 | }
32 |
33 |
34 | request()async{
35 | Map permissions = await [
36 | Permission.location,
37 | ].request();
38 |
39 | List results = permissions.values.toList()
40 | .map((status) => status == PermissionStatus.granted).toList();
41 | if(results == null || results.contains(false)){
42 | showToast('permissions denied');
43 | setState(() {
44 | status = '权限拒绝';
45 | });
46 | }else{
47 | showToast('permissions granted');
48 | setState(() {
49 | status = '权限通过';
50 | });
51 | ///do stuff
52 | }
53 |
54 | }
55 |
56 | }
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/lib/page/demo_page/other/scroll_page.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/10
4 | */
5 |
6 | import 'package:flutter/material.dart';
7 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
8 |
9 |
10 |
11 | class ScrollPageState extends PageState {
12 |
13 | String text = "停止中";
14 | String t = "滚动中";
15 |
16 | //ScrollController scrollController;
17 | @override
18 | void initState() {
19 |
20 | super.initState();
21 | // scrollController = ScrollController();
22 | // scrollController.addListener(() {
23 | // text='停止了...';
24 | // if(scrollController.position.isScrollingNotifier.value){
25 | // text = "滚动中";
26 | // setState(() {
27 | //
28 | // });
29 | // return;
30 | // }
31 | //
32 | // setState(() {
33 | //
34 | // });
35 | // });
36 |
37 | }
38 |
39 |
40 | @override
41 | Widget build(BuildContext context) {
42 | return switchStatusBar2Dark(child: Container(
43 | width: getWidthPx(750),height: getHeightPx(1334),
44 | child: Stack(
45 | children: [
46 | NotificationListener(
47 | onNotification: (ScrollStartNotification startScroll){
48 | debugPrint("开始滚动");
49 | debugPrint("$startScroll");
50 | setState(() {
51 | text = "开始滚动";
52 | });
53 | return true;///false 事件继续冒泡
54 | },
55 | child: NotificationListener(
56 | onNotification: (ScrollEndNotification endNotification){
57 | debugPrint("停止滚动");
58 | debugPrint("$endNotification");
59 | setState(() {
60 | text = "停止滚动";
61 | });
62 | return true;///false 事件继续冒泡
63 | },
64 | child: ListView(
65 | children:buildList(),
66 | ),
67 | ),
68 | ),
69 | Container(
70 | color: Colors.white,
71 | alignment: Alignment.center,
72 | height: getWidthPx(120),
73 | margin: EdgeInsets.only(top: getWidthPx(100)),
74 | child: Text(text
75 | ,style: TextStyle(color: Colors.black),),
76 | ),
77 | ],
78 | ),
79 | ));
80 | }
81 |
82 | List buildList(){
83 | List list = [];
84 | List.generate(20, (index) {
85 | list.add(Container(
86 | width: getWidthPx(750),height: getWidthPx(200),color: index%2==0?Colors.blue:Colors.red,
87 | ));
88 | });
89 | return list;
90 | }
91 |
92 |
93 | }
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/lib/page/demo_page/other/selector_demo_page.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_bedrock/base_framework/ui/widget/provider_widget.dart';
6 | import 'package:flutter_bedrock/base_framework/view_model/single_view_state_model.dart';
7 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
8 | import 'package:provider/provider.dart';
9 |
10 | class _DemoViewModel extends SingleViewStateModel {
11 |
12 | int selectorValue = 1;
13 |
14 | void updateSelectorValue() {
15 | selectorValue++;
16 | notifyListeners(refreshSelector: true);
17 | }
18 |
19 | int pageValue = 1;
20 |
21 | void updatePageValue() {
22 | pageValue++;
23 | notifyListeners();
24 | }
25 |
26 | @override
27 | Future? loadData() {
28 |
29 | }
30 |
31 | @override
32 | onCompleted(data) {
33 |
34 | }
35 |
36 | }
37 |
38 | class SelectorDemo extends PageState{
39 | late _DemoViewModel vm;
40 |
41 | @override
42 | void initState() {
43 | super.initState();
44 | vm = _DemoViewModel();
45 | }
46 |
47 | ///注意查看两个按钮所触发的 debugPrint的输出
48 |
49 | @override
50 | Widget build(BuildContext context) {
51 | return switchStatusBar2Dark(
52 | child: ProviderWidget<_DemoViewModel>(
53 | builder: (ctx,model,child) {
54 | debugPrint('build page');
55 | return _buildPage();
56 | },
57 | model: vm));
58 | }
59 |
60 | Widget _buildPage() {
61 | return Container(
62 | color: Colors.white,
63 | child: Column(
64 | mainAxisAlignment: MainAxisAlignment.center,
65 | children: [
66 | Text('${vm.pageValue}',),
67 | getSizeBox(height: 40),
68 | RaisedButton(onPressed: () {
69 | vm.updatePageValue();
70 | },child: Text('refresh page'),),
71 |
72 | Selector<_DemoViewModel,int>(
73 | selector: (ctx,model) {
74 | return model.selectorValue;
75 | },
76 | builder: (ctxx,value,child) {
77 | debugPrint('build selector');
78 | return Text('$value',);
79 | },
80 | ),
81 | getSizeBox(height: 40),
82 | RaisedButton(onPressed: () {
83 | vm.updateSelectorValue();
84 | },child: Text('refresh selector'),),
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/other/simple_list/simple_list_page.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
6 | import 'package:flutter_bedrock/base_framework/widget_state/widget_state.dart';
7 |
8 | class SimpleListPage extends PageState{
9 | @override
10 | Widget build(BuildContext context) {
11 | return switchStatusBar2Dark(child: Container(
12 | width: 750.w,height: 1334.h,
13 | child: ListView(
14 | children: List.generate(40, (index) => generateWidget(()=>_ListItem(index))).toList(),
15 | //children: List.generate(40, (index) => _NormalItem(index: index,)).toList(),
16 | ),
17 | ));
18 | }
19 |
20 | }
21 |
22 | class _NormalItem extends StatefulWidget{
23 | final int index;
24 |
25 | const _NormalItem({Key? key, this.index = 0}) : super(key: key);
26 |
27 | @override
28 | State createState() {
29 | return _NormalItemState();
30 | }
31 | }
32 |
33 | class _NormalItemState extends State<_NormalItem> {
34 | @override
35 | Widget build(BuildContext context) {
36 | return Container(
37 | height: 200.w,color: widget.index % 2 == 0 ? Colors.blue : Colors.red,
38 | );
39 | }
40 | }
41 |
42 |
43 | class _ListItem extends WidgetState{
44 |
45 | int index;
46 |
47 | _ListItem(this.index);
48 |
49 | @override
50 | Widget build(BuildContext context) {
51 | return Container(
52 | height: 200.w,color: index % 2 == 0 ? Colors.blue : Colors.red,
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/other/test_android_page.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/12/14
4 | */
5 | import 'package:flutter/material.dart';
6 | import 'package:flutter_bedrock/base_framework/native/native_method_manager.dart';
7 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
8 | class TestAndroidPage extends PageState{
9 | @override
10 | Widget build(BuildContext context) {
11 | return switchStatusBar2Dark(child: Container(
12 | color: Colors.white,
13 | child: Column(
14 | mainAxisAlignment: MainAxisAlignment.center,
15 | children: [
16 | ElevatedButton(onPressed: (){
17 | NativeMethodManager.getInstance()!.throwChildThreadException();
18 |
19 | },child: Text('android : 抛一个子线程异常'),),
20 | getSizeBox(height: getWidthPx(40)),
21 | Text("默认 ui线程保护 和 activity 启动保护是注释掉的,需要在原生端的 BaseApp开启,才能进行下面的测试。\n"
22 | "PS:你可以先点击下方的看一下效果,然后开启后再看一下效果",
23 | style: TextStyle(color: Colors.black,fontSize: getSp(32)),),
24 | getSizeBox(height: getWidthPx(40)),
25 | ElevatedButton(onPressed: (){
26 | NativeMethodManager.getInstance()!.throwUiThreadException();
27 |
28 | },child: Text('android : 抛一个子UI线程异常'),),
29 | getSizeBox(height: getWidthPx(40)),
30 | ElevatedButton(onPressed: (){
31 | NativeMethodManager.getInstance()!.throwStartUpException();
32 | },child: Text('android : 抛一个生命周期异常'),),
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/other/timer_page.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import 'dart:async';
5 |
6 | import 'package:flutter/material.dart';
7 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
8 |
9 |
10 |
11 | class TimerPageState extends PageState {
12 |
13 | String text = '''
14 | 有的时候需要在用户滚动到某个item后请求接口并刷新,但是并不能确定用户会停在这个item。如果不考虑这些我,
15 | 只是生硬的停一次请求一次,\n1、造成卡顿 \n 2、引起数据错乱 \n 3、浪费服务器资源 \n
16 | 我的做法是通过timer来进行请求\n
17 | 具体请查看代码和日志
18 | ''';
19 |
20 | @override
21 | Widget build(BuildContext context) {
22 | return switchStatusBar2Dark(child: Container(
23 | color: Colors.white,
24 | width: getWidthPx(750),height: getHeightPx(1334),
25 | child: Column(
26 | mainAxisAlignment: MainAxisAlignment.center,
27 | children: [
28 | Text(text,style: TextStyle(color: Colors.black,fontSize: getSp(28)),),
29 | ElevatedButton(
30 | child: Text('请求一下接口'),
31 | onPressed: (){
32 | request();
33 | },
34 | ),
35 | ],
36 | ),
37 | ));
38 | }
39 |
40 | Timer? singleTimer;
41 |
42 | request(){
43 | if(singleTimer != null && singleTimer!.isActive){
44 | debugPrint('取消上一次请求');
45 | singleTimer!.cancel();
46 | }
47 | ///假设用户停止500ms后 请求
48 | singleTimer = Timer(Duration(milliseconds: 500),()async{
49 |
50 | await Future.delayed((Duration(seconds: 1))).then((value) => debugPrint("请求结束"));
51 |
52 | ///请求结束后的刷新
53 | ///接口请求应放在model 层,那么直接调用notifierListener()即可。
54 | ///其他地方请注意内存泄露问题
55 | debugPrint('刷新');
56 | });
57 | }
58 |
59 | }
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/lib/page/demo_page/route_anim/fade_page.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
5 |
6 |
7 |
8 | class FadePageState extends PageState {
9 | @override
10 | Widget build(BuildContext context) {
11 | return switchStatusBar2Dark(child: Container(
12 | color: Colors.orangeAccent,width: getWidthPx(750),height: getHeightPx(1334),
13 | alignment: Alignment.center,
14 | child: Text("fade page"),
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/route_anim/route_animation_page.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_bedrock/base_framework/factory/page/page_animation_builder.dart';
6 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
7 | import 'package:flutter_bedrock/page/demo_page/route_anim/fade_page.dart';
8 | import 'package:flutter_bedrock/page/demo_page/route_anim/scale_page.dart';
9 | import 'package:flutter_bedrock/page/demo_page/route_anim/slide_page.dart';
10 |
11 |
12 | class RouteAnimationPageState extends PageState {
13 | @override
14 | Widget build(BuildContext context) {
15 | return switchStatusBar2Dark(
16 | child: Container(width: getWidthPx(750),height: getHeightPx(1334),
17 | child: Column(
18 | mainAxisAlignment: MainAxisAlignment.center,
19 | children: [
20 | ElevatedButton(
21 | onPressed: (){
22 | push(SlidePageState(),animation: PageAnimation.Slide);
23 |
24 | },
25 | child: Text("滑动跳转"),
26 | ),
27 | getSizeBox(height: getWidthPx(40)),
28 | ElevatedButton(
29 | onPressed: (){
30 | push(ScalePageState(),animation: PageAnimation.Scale);
31 |
32 | },
33 | child: Text("缩放跳转"),
34 | ),
35 | getSizeBox(height: getWidthPx(40)),
36 | ElevatedButton(
37 | onPressed: (){
38 | push(FadePageState(),animation: PageAnimation.Fade);
39 |
40 | },
41 | child: Text("渐隐跳转"),
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/route_anim/scale_page.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 | import 'package:flutter/cupertino.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
6 |
7 |
8 |
9 | class ScalePageState extends PageState {
10 | @override
11 | Widget build(BuildContext context) {
12 | return switchStatusBar2Dark(child: Container(
13 | color: Colors.red,width: getWidthPx(750),height: getHeightPx(1334),
14 | alignment: Alignment.center,
15 | child: Text("scale page"),
16 | ));
17 | }
18 | }
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/lib/page/demo_page/route_anim/slide_page.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
6 |
7 | class SlidePageState extends PageState {
8 | @override
9 | Widget build(BuildContext context) {
10 | return switchStatusBar2Dark(child: Container(
11 | color: Colors.blue,
12 | child: Column(
13 | mainAxisAlignment: MainAxisAlignment.center,
14 | children: [
15 | Text("滑动页面"),
16 | ],
17 | )),
18 | );
19 | }
20 | }
--------------------------------------------------------------------------------
/lib/page/demo_page/slide_out_page.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/6/12
4 | */
5 |
6 |
7 | import 'package:flutter/material.dart';
8 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
9 |
10 |
11 | class SlideOutPageState extends PageState {
12 |
13 | String intro = '''
14 | switchStatusBar2Dark方法默认是使用侧滑退出的,如果不需要请将needSlideOut设置为false。
15 | 对于使用侧滑退出的页面,请在routeManager中用SlideRightRouteBuilder对页面进行包裹,这样可以保证
16 | 页面在退出和进入的时候,保证视觉上的流畅性。\n
17 | 侧滑触发,是在页面左侧十分之一屏幕宽度内按下向右滑动超过二分之一屏幕宽度后松开退出。\n\n
18 | 如果整个项目不打算接入侧滑退出,可以直接将switchStatusBar2Dark方法默认值改为false
19 | ''';
20 |
21 | @override
22 | Widget build(BuildContext context) {
23 | return switchStatusBar2Dark(
24 | needSlideOut: true,
25 | child: Container(
26 | color: Colors.blue,
27 | width: getWidthPx(750),height: getHeightPx(1334),
28 | padding: EdgeInsets.symmetric(horizontal: getWidthPx(40)),
29 | child: Column(
30 | mainAxisAlignment: MainAxisAlignment.center,
31 | children: [
32 | Text("slide page out"),
33 | getSizeBox(height: getWidthPx(100)),
34 | Text(intro,style: TextStyle(color: Colors.yellow,
35 | fontSize: getSp(30)),)
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/start/pages/page_1.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/9/10
4 | */
5 |
6 |
7 |
8 |
9 | import 'package:flutter/material.dart';
10 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
11 | import 'package:flutter_bedrock/page/demo_page/start/pages/page_2.dart';
12 |
13 | class Page1 extends PageState{
14 | @override
15 | Widget build(BuildContext context) {
16 | return switchStatusBar2Dark(child: Container(
17 | child: Column(
18 | mainAxisAlignment: MainAxisAlignment.center,
19 | children: [
20 | getSizeBox(height: getHeightPx(100)),
21 | Text('page 1',style: TextStyle(color: Colors.black,fontSize: getSp(24)),),
22 | getSizeBox(height: getHeightPx(40)),
23 | ElevatedButton(
24 | child: Text('go page 2',style: TextStyle(color: Colors.black),),
25 | onPressed: (){
26 | push(Page2());
27 | },
28 | ),
29 | ],
30 | ),
31 | ));
32 | }
33 |
34 |
35 | }
--------------------------------------------------------------------------------
/lib/page/demo_page/start/pages/page_2.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/9/10
4 | */
5 |
6 |
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
10 | import 'package:flutter_bedrock/page/demo_page/start/pages/page_3.dart';
11 | import 'package:flutter_bedrock/page/demo_page/start/start_page.dart';
12 |
13 | class Page2 extends PageState{
14 | @override
15 | Widget build(BuildContext context) {
16 | return switchStatusBar2Dark(child: Container(
17 | child: Column(
18 | mainAxisAlignment: MainAxisAlignment.center,
19 | children: [
20 | getSizeBox(height: getHeightPx(100)),
21 | Text('page 2',style: TextStyle(color: Colors.black,fontSize: getSp(24)),),
22 | getSizeBox(height: getHeightPx(40)),
23 | ElevatedButton(
24 | child: Text('go page 3',style: TextStyle(color: Colors.black),),
25 | onPressed: (){
26 | pushAndRemoveUntil(Page3(),predicate: (route)=>route.settings.name == '$StartPage');
27 | },
28 | ),
29 | ],
30 | ),
31 | ));
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/lib/page/demo_page/start/pages/page_3.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/9/10
4 | */
5 |
6 |
7 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
8 | import 'package:flutter/material.dart';
9 |
10 | class Page3 extends PageState{
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | return switchStatusBar2Dark(child: Container(
15 | child: Column(
16 | mainAxisAlignment: MainAxisAlignment.center,
17 | children: [
18 | getSizeBox(height: getHeightPx(100)),
19 | Text('page 3',style: TextStyle(color: Colors.black,fontSize: getSp(24)),),
20 | getSizeBox(height: getHeightPx(40)),
21 | ElevatedButton(
22 | child: Text('pop',style: TextStyle(color: Colors.black),),
23 | onPressed: (){
24 | pop();
25 | },
26 | ),
27 | ],
28 | ),
29 | ));
30 | }
31 |
32 |
33 | }
--------------------------------------------------------------------------------
/lib/page/demo_page/start/start_page.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/9/10
4 | */
5 |
6 |
7 |
8 |
9 | import 'package:flutter/material.dart';
10 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
11 | import 'package:flutter_bedrock/page/demo_page/start/pages/page_1.dart';
12 | import 'package:flutter_bedrock/page/demo_page/start/with_value_page.dart';
13 |
14 | class StartPage extends PageState{
15 |
16 | String info ='添加页面时,重写build方法,总会引入framework\n官方不推荐引入这个(虽然不会出错)\n'
17 | '建议在(AS)File-setting-editor-live templates设置快捷键引入 Material或者cupertinol';
18 |
19 |
20 | String backValue = '等待WithValuePage的返回值';
21 | @override
22 | Widget build(BuildContext context) {
23 | return switchStatusBar2Dark(
24 | child: Container(
25 | child: Column(
26 | mainAxisAlignment: MainAxisAlignment.center,
27 | children: [
28 | getSizeBox(height: getHeightPx(100)),
29 | Text('这里是基本的页面创建和路由操作,请参考代码来学习',style: TextStyle(color: Colors.black),),
30 | getSizeBox(height: getHeightPx(20)),
31 | Text(info,style: TextStyle(color: Colors.black),),
32 | getSizeBox(height: getHeightPx(40)),
33 | ElevatedButton(
34 | child: Text('go page 1 with pushAndRemoveUntil',style: TextStyle(color: Colors.black),),
35 | onPressed: (){
36 | push(Page1());
37 | },
38 | ),
39 | Container(
40 | width: getWidthPx(750),height: getHeightPx(10),
41 | color: Colors.green,
42 | ),
43 | getSizeBox(height: getHeightPx(40)),
44 | Text(backValue,style: TextStyle(color: Colors.black,fontSize: getSp(24)),),
45 | ElevatedButton(
46 | child: Text('push with value',style: TextStyle(color: Colors.black),),
47 | onPressed: (){
48 | push(WithValuePage('start page送了一个西瓜'))
49 | .then((value){
50 | setState(() {
51 | backValue = value??'啥都没给';
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 |
--------------------------------------------------------------------------------
/lib/page/demo_page/start/with_value_page.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/9/10
4 | */
5 |
6 |
7 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
8 | import 'package:flutter/material.dart';
9 |
10 | class WithValuePage extends PageState{
11 |
12 | final String value;
13 |
14 | WithValuePage(this.value);
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return switchStatusBar2Dark(child: Container(
19 | child: Column(
20 | mainAxisAlignment: MainAxisAlignment.center,
21 | children: [
22 | getSizeBox(height: getHeightPx(100)),
23 | Text(value,style: TextStyle(color: Colors.black,fontSize: getSp(24)),),
24 | getSizeBox(height: getHeightPx(40)),
25 | ElevatedButton(
26 | child: Text('go page 2',style: TextStyle(color: Colors.black),),
27 | onPressed: (){
28 | pop(result: 'WithValuePage页面返回了一个苹果');
29 | },
30 | ),
31 | ],
32 | ),
33 | ));
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/lib/page/demo_widget/custom_drawer.dart:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | import 'package:flutter/cupertino.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:flutter_bedrock/base_framework/widget_state/widget_state.dart';
7 |
8 | class CustomBottomDrawerWidget extends WidgetState with SingleTickerProviderStateMixin{
9 |
10 | AnimationController? animationController;
11 | late Animation animation;
12 |
13 | double bottom = -600;
14 |
15 | @override
16 | void initState() {
17 | animationController = AnimationController(vsync: this,duration: Duration(milliseconds: 500));
18 | animation =Tween(begin: bottom,end: 0).animate(animationController!);
19 | super.initState();
20 | animationController!.addListener(() {
21 | setState(() {
22 | bottom = animation.value;
23 | });
24 | });
25 |
26 | WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
27 | animationController!.forward();
28 | });
29 | }
30 | @override
31 | void dispose() {
32 | animationController!.dispose();
33 | // TODO: implement dispose
34 | super.dispose();
35 | }
36 |
37 |
38 |
39 | @override
40 | Widget build(BuildContext context) {
41 |
42 | return Stack(
43 | alignment: Alignment.bottomCenter,
44 | children: [
45 | Positioned(
46 | bottom: getHeightPx(bottom),
47 | child: Material(
48 | color: const Color.fromRGBO(0, 0, 0, 0),
49 | child: Container(
50 | width: getWidthPx(750),height: getHeightPx(600),
51 | decoration: BoxDecoration(
52 | color: Colors.white,
53 | borderRadius: BorderRadius.only(topLeft: Radius.circular(getHeightPx(16)),topRight: Radius.circular(getHeightPx(16))),
54 | ),
55 | alignment: Alignment.center,
56 | child: Column(
57 | mainAxisAlignment: MainAxisAlignment.center,
58 | children: [
59 | Text('this is a drawer.\n slide from bottom',style: TextStyle(
60 | color: Colors.black,fontSize: getSp(32)
61 | ),),
62 | getSizeBox(height: getWidthPx(70)),
63 | ElevatedButton(
64 | onPressed: (){
65 | animationController!.reverse().then((value) => Navigator.pop(context));
66 | },
67 | child: Text('close',style: TextStyle(color: Colors.black,fontSize: getSp(30)),),
68 | ),
69 | ],
70 | ),
71 | ),
72 | ),
73 | )
74 | ],
75 | );
76 | }
77 |
78 | }
--------------------------------------------------------------------------------
/lib/page/exception/exception_page.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/5/26
4 | */
5 |
6 |
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_bedrock/base_framework/widget_state/page_state.dart';
10 |
11 | ///非业务型异常展示的页面
12 |
13 |
14 | class ExceptionPageState extends PageState {
15 |
16 | final String exception;
17 | final String stack;
18 |
19 | ExceptionPageState(this.exception, this.stack);
20 |
21 |
22 | @override
23 | Widget build(BuildContext context) {
24 |
25 | return switchStatusBar2Dark(
26 | isSetDark: true,
27 | child: Container(
28 | color: Colors.white,
29 | width: getWidthPx(750),height: getHeightPx(1334),
30 | child: SingleChildScrollView(
31 | child: Column(
32 | mainAxisAlignment: MainAxisAlignment.center,
33 | children: [
34 | Text(exception,style: TextStyle(color: Colors.black),),
35 | getSizeBox(height: getWidthPx(50)),
36 | Text(stack,style: TextStyle(color: Colors.blue),),
37 | ],
38 | ),
39 | ),
40 | ));
41 | }
42 | }
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/lib/page/mine/entity/user_entity.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/5/18
4 | */
5 |
6 | class UserEntity {
7 | String? nickName;
8 | String? id;
9 |
10 | UserEntity({this.nickName, this.id});
11 |
12 | UserEntity.fromJson(Map json) {
13 | nickName = json['nick_name'];
14 | id = json['id'];
15 | }
16 |
17 | Map toJson() {
18 | final Map data = new Map();
19 | data['nick_name'] = this.nickName;
20 | data['id'] = this.id;
21 | return data;
22 | }
23 | }
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/lib/service_api/bedrock_repository_proxy.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Author : LiJiqqi
3 | * Date : 2020/5/28
4 | */
5 |
6 |
7 | import 'package:flutter_bedrock/service_api/section_one_api.dart';
8 |
9 | class BedrockRepositoryProxy{
10 | static BedrockRepositoryProxy? _singleton;
11 |
12 | BedrockRepositoryProxy._internal(){
13 | //do something
14 | }
15 |
16 | static BedrockRepositoryProxy? getInstance(){
17 | if(_singleton == null){
18 | _singleton = BedrockRepositoryProxy._internal();
19 | }
20 | return _singleton;
21 | }
22 |
23 |
24 | ///项目按模块划分 api eg: 我的、首页、商品等等
25 | SectionOne getSectionOne(){
26 | return SectionOne();
27 | }
28 |
29 | }
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/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 that Flutter provides. 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 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:flutter_bedrock/main.dart';
12 |
13 | void main() {
14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(MyApp());
17 |
18 | // Verify that our counter starts at 0.
19 | expect(find.text('0'), findsOneWidget);
20 | expect(find.text('1'), findsNothing);
21 |
22 | // Tap the '+' icon and trigger a frame.
23 | await tester.tap(find.byIcon(Icons.add));
24 | await tester.pump();
25 |
26 | // Verify that our counter has incremented.
27 | expect(find.text('0'), findsNothing);
28 | expect(find.text('1'), findsOneWidget);
29 | });
30 | }
31 |
--------------------------------------------------------------------------------