├── .gitignore ├── .metadata ├── README.md ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── flutter_app │ │ │ │ ├── MainActivity.java │ │ │ │ ├── MyApp.java │ │ │ │ └── RsaAndBase64 │ │ │ │ ├── RSAUtils.java │ │ │ │ └── RsaAndBase64.java │ │ └── res │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── launch_image.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── app.apk ├── assets └── images │ ├── 2.0x │ ├── ico_account.png │ ├── ico_homepage_clock.png │ ├── ico_homepage_jifen_btn.png │ ├── ico_homepage_qiandao_btn.png │ ├── ico_homepage_xinfeng.png │ ├── ico_kefu_btn.png │ ├── ico_loading_bg.png │ ├── ico_login_bg.png │ ├── ico_login_close.png │ ├── ico_minepage_class.png │ ├── ico_minepage_item_arrow.png │ ├── ico_minepage_jifen.png │ ├── ico_minepage_shezhi.png │ ├── ico_minepage_shouchang.png │ ├── ico_minepage_xiaoxi.png │ ├── ico_minepage_yijian.png │ ├── ico_person_clerk.png │ ├── ico_person_doctor.png │ ├── ico_person_normal.png │ ├── ico_person_nurse.png │ ├── ico_person_physician.png │ ├── ico_person_sales.png │ ├── ico_pwd.png │ ├── ico_qiandao_bg.png │ ├── ico_tabar_class_normal.png │ ├── ico_tabar_class_pressed.png │ ├── ico_tabar_home_normal.png │ ├── ico_tabar_home_pressed.png │ ├── ico_tabar_mine_normal.png │ ├── ico_tabar_mine_pressed.png │ ├── ico_tabar_prefecture_normal.png │ └── ico_tabar_prefecture_pressed.png │ ├── 3.0x │ ├── ico_account.png │ ├── ico_homepage_clock.png │ ├── ico_homepage_jifen_btn.png │ ├── ico_homepage_qiandao_btn.png │ ├── ico_homepage_xinfeng.png │ ├── ico_kefu_btn.png │ ├── ico_loading_bg.png │ ├── ico_login_bg.png │ ├── ico_login_close.png │ ├── ico_minepage_class.png │ ├── ico_minepage_item_arrow.png │ ├── ico_minepage_jifen.png │ ├── ico_minepage_shezhi.png │ ├── ico_minepage_shouchang.png │ ├── ico_minepage_xiaoxi.png │ ├── ico_minepage_yijian.png │ ├── ico_person_clerk.png │ ├── ico_person_doctor.png │ ├── ico_person_normal.png │ ├── ico_person_nurse.png │ ├── ico_person_physician.png │ ├── ico_person_sales.png │ ├── ico_pwd.png │ ├── ico_qiandao_bg.png │ ├── ico_tabar_class_normal.png │ ├── ico_tabar_class_pressed.png │ ├── ico_tabar_home_normal.png │ ├── ico_tabar_home_pressed.png │ ├── ico_tabar_mine_normal.png │ ├── ico_tabar_mine_pressed.png │ ├── ico_tabar_prefecture_normal.png │ └── ico_tabar_prefecture_pressed.png │ ├── ic_arrow_right.png │ ├── ic_back_black.png │ ├── ico_account.png │ ├── ico_back.png │ ├── ico_dengji_bg.png │ ├── ico_dengji_bg_short.png │ ├── ico_homepage_clock.png │ ├── ico_homepage_comment_gray.png │ ├── ico_homepage_eye_gray.png │ ├── ico_homepage_jifen_btn.png │ ├── ico_homepage_qiandao_btn.png │ ├── ico_homepage_xinfeng.png │ ├── ico_kefu_btn.png │ ├── ico_loading_bg.png │ ├── ico_login_bg.png │ ├── ico_login_close.png │ ├── ico_minepage_class.png │ ├── ico_minepage_item_arrow.png │ ├── ico_minepage_jifen.png │ ├── ico_minepage_shezhi.png │ ├── ico_minepage_shouchang.png │ ├── ico_minepage_xiaoxi.png │ ├── ico_minepage_yijian.png │ ├── ico_modal_close_btn.png │ ├── ico_person_clerk.png │ ├── ico_person_doctor.png │ ├── ico_person_normal.png │ ├── ico_person_nurse.png │ ├── ico_person_physician.png │ ├── ico_person_sales.png │ ├── ico_pwd.png │ ├── ico_qiandao_bg.png │ ├── ico_qiandao_tag.png │ ├── ico_qiandao_yiqian_gou.png │ ├── ico_sex_nan.png │ ├── ico_sex_nv.png │ ├── ico_tabar_class_normal.png │ ├── ico_tabar_class_pressed.png │ ├── ico_tabar_home_normal.png │ ├── ico_tabar_home_pressed.png │ ├── ico_tabar_mine_normal.png │ ├── ico_tabar_mine_pressed.png │ ├── ico_tabar_prefecture_normal.png │ ├── ico_tabar_prefecture_pressed.png │ ├── loading.gif │ └── wechat_friends.png ├── flutter_1.png ├── flutter_2.png ├── flutter_3.png ├── flutter_4.png ├── 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.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── icon-1024.png │ │ ├── icon-20@2x.png │ │ ├── icon-20@3x.png │ │ ├── icon-29@2x.png │ │ ├── icon-29@3x.png │ │ ├── icon-40@2x.png │ │ ├── icon-40@3x.png │ │ ├── icon-60@2x.png │ │ └── icon-60@3x.png │ ├── Contents.json │ ├── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── README.md │ │ ├── splash_logo_one.png │ │ ├── splash_logo_one@2x.png │ │ └── splash_logo_one@3x.png │ └── splash_logo.imageset │ │ ├── Contents.json │ │ ├── splash_logo.png │ │ ├── splash_logo@2x.png │ │ └── splash_logo@3x.png │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── RsaAndBase64 │ ├── RSAEncryptor.h │ ├── RSAEncryptor.m │ ├── RsaAndBase64.h │ └── RsaAndBase64.m │ └── main.m ├── lib ├── app.dart ├── global_store │ ├── action.dart │ ├── reducer.dart │ ├── state.dart │ └── store.dart ├── main.dart ├── models │ ├── entity_factory.dart │ ├── home │ │ ├── material_entity.dart │ │ └── plate_entity.dart │ ├── index │ │ └── page_component.dart │ ├── mine │ │ └── collect_entity.dart │ └── user_info_entity.dart ├── res │ ├── colors.dart │ ├── dimens.dart │ ├── gaps.dart │ ├── resources.dart │ └── styles.dart ├── routes │ ├── class_module │ │ └── class_page │ │ │ ├── action.dart │ │ │ ├── effect.dart │ │ │ ├── page.dart │ │ │ ├── reducer.dart │ │ │ ├── state.dart │ │ │ └── view.dart │ ├── home_module │ │ ├── home_page │ │ │ ├── action.dart │ │ │ ├── effect.dart │ │ │ ├── page.dart │ │ │ ├── reducer.dart │ │ │ ├── state.dart │ │ │ ├── view.dart │ │ │ └── widget │ │ │ │ ├── home_page_item.dart │ │ │ │ └── home_page_list_tile.dart │ │ ├── news_detail │ │ │ ├── action.dart │ │ │ ├── effect.dart │ │ │ ├── page.dart │ │ │ ├── reducer.dart │ │ │ ├── state.dart │ │ │ └── view.dart │ │ └── qiandao_alert.dart │ ├── index │ │ ├── action.dart │ │ ├── effect.dart │ │ ├── page.dart │ │ ├── reducer.dart │ │ ├── state.dart │ │ └── view.dart │ ├── login_page │ │ ├── action.dart │ │ ├── auth_button │ │ │ ├── action.dart │ │ │ ├── component.dart │ │ │ ├── effect.dart │ │ │ ├── reducer.dart │ │ │ ├── state.dart │ │ │ └── view.dart │ │ ├── effect.dart │ │ ├── page.dart │ │ ├── reducer.dart │ │ ├── state.dart │ │ └── view.dart │ ├── mine_module │ │ ├── collect_page │ │ │ ├── action.dart │ │ │ ├── effect.dart │ │ │ ├── page.dart │ │ │ ├── reducer.dart │ │ │ ├── state.dart │ │ │ └── view.dart │ │ ├── exit_dialog.dart │ │ ├── mine_page │ │ │ ├── action.dart │ │ │ ├── effect.dart │ │ │ ├── page.dart │ │ │ ├── reducer.dart │ │ │ ├── state.dart │ │ │ └── view.dart │ │ └── setting_page │ │ │ ├── action.dart │ │ │ ├── effect.dart │ │ │ ├── page.dart │ │ │ ├── reducer.dart │ │ │ ├── state.dart │ │ │ └── view.dart │ └── prefecture_module │ │ └── prefecture_page │ │ ├── action.dart │ │ ├── effect.dart │ │ ├── page.dart │ │ ├── reducer.dart │ │ ├── state.dart │ │ └── view.dart ├── utils │ ├── common.dart │ ├── data_tool.dart │ ├── double_tap_back_exit_app.dart │ ├── i18n.dart │ ├── image_utils.dart │ ├── log_utils.dart │ ├── native_method.dart │ ├── net │ │ ├── base_entity.dart │ │ ├── dio_utils.dart │ │ ├── error_handle.dart │ │ ├── http_api.dart │ │ ├── intercept.dart │ │ └── net.dart │ ├── progress.dart │ ├── routes │ │ ├── 404.dart │ │ ├── application.dart │ │ ├── fluro_navigator.dart │ │ ├── router_init.dart │ │ ├── routers.dart │ │ └── webview_page.dart │ ├── screen_util.dart │ ├── theme_utils.dart │ └── toast.dart └── widgets │ ├── app_bar.dart │ ├── keep_alive_widget.dart │ ├── list_subtitle.dart │ ├── load_image.dart │ ├── state_layout.dart │ └── top_header.dart ├── pubspec.lock └── pubspec.yaml /.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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fish-redux-demo 2 | 3 | 一个基于fish-redux的Flutter脚手架,包含基本的屏幕适配、全局toast提示、网络请求封装、路由框架集成、网络图片缓存, 4 | 实现了在fish-redux框架下fluro路由框架的集成、演示了Flutter和WebView的双向通信,实现了在fish-redux模式下的页面跳转 5 | 传参以及实现AutomaticKeepAliveClientMixin,以及实现了父组件和子组件局部数据和页面之间全局数据共享逻辑,是一个开箱即用不可多得的Flutter项目脚手架。 6 | (出于业务安全考虑,Android和iOS原生端的加密公钥已经被抹掉请直接下载[Android release 安装包](https://github.com/bozaigao/fish-redux-demo/blob/master/app.apk)进行体验,账号:11211110085 密码:123456) 7 | 8 | ## 效果图如下 9 | ![](./flutter_1.png) 10 | ![](./flutter_2.png) 11 | ![](./flutter_3.png) 12 | ![](./flutter_4.png) 13 | 14 | Taro适配rn、h5、微信小程序友情链接[Taro-demo](https://github.com/bozaigao/Taro-demo) -------------------------------------------------------------------------------- /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 from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 28 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "com.example.flutter_app" 37 | minSdkVersion 16 38 | targetSdkVersion 28 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | testImplementation 'junit:junit:4.12' 59 | androidTestImplementation 'androidx.test:runner:1.1.1' 60 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 61 | } 62 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 18 | 26 | 29 | 30 | 31 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/example/flutter_app/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.flutter_app; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.example.flutter_app.RsaAndBase64.RsaAndBase64; 6 | 7 | import io.flutter.embedding.android.FlutterActivity; 8 | import io.flutter.embedding.engine.FlutterEngine; 9 | import io.flutter.plugins.GeneratedPluginRegistrant; 10 | 11 | public class MainActivity extends FlutterActivity { 12 | @Override 13 | public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { 14 | GeneratedPluginRegistrant.registerWith(flutterEngine); 15 | //rsa加密 16 | RsaAndBase64.registerWith(flutterEngine); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/example/flutter_app/MyApp.java: -------------------------------------------------------------------------------- 1 | package com.example.flutter_app; 2 | 3 | import io.flutter.app.FlutterApplication; 4 | 5 | /** 6 | * @filename MyApp.java 7 | * @author 何晏波 8 | * @QQ 1054539528 9 | * @date 2020-01-16 10 | * @Description: FlutterApplication 11 | */ 12 | public class MyApp extends FlutterApplication { 13 | 14 | @Override 15 | public void onCreate() { 16 | super.onCreate(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/example/flutter_app/RsaAndBase64/RsaAndBase64.java: -------------------------------------------------------------------------------- 1 | package com.example.flutter_app.RsaAndBase64; 2 | 3 | import android.util.Base64; 4 | import android.util.Log; 5 | 6 | import java.security.PublicKey; 7 | import java.util.regex.Matcher; 8 | import java.util.regex.Pattern; 9 | 10 | import javax.crypto.Cipher; 11 | 12 | import io.flutter.embedding.engine.FlutterEngine; 13 | import io.flutter.plugin.common.MethodCall; 14 | import io.flutter.plugin.common.MethodChannel; 15 | 16 | /** 17 | * @author 何晏波 18 | * @filename RsaAndBase64.java 19 | * @QQ 1054539528 20 | * @date 2018/8/11 21 | * @Description: 加密模块 22 | */ 23 | public class RsaAndBase64 implements MethodChannel.MethodCallHandler { 24 | public static final String CHANNEL = "www.guigug.com/rsa_and_base64"; 25 | static MethodChannel channel; 26 | private final String ECB_PKCS1_PADDING = "RSA/ECB/PKCS1Padding";//加密填充方式 27 | //rsa加密公钥 28 | //处于业务安全考虑,该公钥已被隐藏 29 | private final String public_key = "xxx"; 30 | 31 | public static void registerWith(FlutterEngine flutterEngine) { 32 | channel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL); 33 | RsaAndBase64 instance = new RsaAndBase64(); 34 | //setMethodCallHandler在此通道上接收方法调用的回调 35 | channel.setMethodCallHandler(instance); 36 | } 37 | 38 | 39 | private String rsaAndBase64Encode(String plaint) { 40 | try { 41 | // 得到公钥 42 | PublicKey publicKey = RSAUtils.loadPublicKey(public_key); 43 | // 加密数据 44 | Cipher cp = Cipher.getInstance(ECB_PKCS1_PADDING); 45 | cp.init(Cipher.ENCRYPT_MODE, publicKey); 46 | Log.e("加密后的数据", replaceBlank(new String(Base64.encode(cp.doFinal(plaint.getBytes()), Base64.DEFAULT)))); 47 | return replaceBlank(new String(Base64.encode(cp.doFinal(plaint.getBytes()), Base64.DEFAULT))); 48 | } catch (Exception e) { 49 | e.printStackTrace(); 50 | return ""; 51 | } 52 | } 53 | 54 | private void decryptByPublicKey(String plaint) throws Exception { 55 | Log.e("TAG", new String(Base64.decode(plaint, Base64.DEFAULT))); 56 | } 57 | 58 | 59 | private String replaceBlank(String str) { 60 | String dest = ""; 61 | if (str != null) { 62 | Pattern p = Pattern.compile("\\s*|\t|\r|\n"); 63 | Matcher m = p.matcher(str); 64 | dest = m.replaceAll(""); 65 | } 66 | return dest; 67 | } 68 | 69 | @Override 70 | public void onMethodCall(MethodCall call, MethodChannel.Result result) { 71 | //接收来自flutter的指令encode 72 | if (call.method.equals("encode")) { 73 | //返回给flutter的参数 74 | result.success(rsaAndBase64Encode(call.argument("plaint"))); 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/launch_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/android/app/src/main/res/mipmap-xhdpi/launch_image.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.5.0' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /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-5.6.2-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 | -------------------------------------------------------------------------------- /app.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/app.apk -------------------------------------------------------------------------------- /assets/images/2.0x/ico_account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_account.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_homepage_clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_homepage_clock.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_homepage_jifen_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_homepage_jifen_btn.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_homepage_qiandao_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_homepage_qiandao_btn.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_homepage_xinfeng.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_homepage_xinfeng.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_kefu_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_kefu_btn.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_loading_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_loading_bg.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_login_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_login_bg.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_login_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_login_close.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_minepage_class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_minepage_class.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_minepage_item_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_minepage_item_arrow.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_minepage_jifen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_minepage_jifen.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_minepage_shezhi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_minepage_shezhi.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_minepage_shouchang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_minepage_shouchang.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_minepage_xiaoxi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_minepage_xiaoxi.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_minepage_yijian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_minepage_yijian.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_person_clerk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_person_clerk.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_person_doctor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_person_doctor.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_person_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_person_normal.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_person_nurse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_person_nurse.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_person_physician.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_person_physician.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_person_sales.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_person_sales.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_pwd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_pwd.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_qiandao_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_qiandao_bg.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_tabar_class_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_tabar_class_normal.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_tabar_class_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_tabar_class_pressed.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_tabar_home_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_tabar_home_normal.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_tabar_home_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_tabar_home_pressed.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_tabar_mine_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_tabar_mine_normal.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_tabar_mine_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_tabar_mine_pressed.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_tabar_prefecture_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_tabar_prefecture_normal.png -------------------------------------------------------------------------------- /assets/images/2.0x/ico_tabar_prefecture_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/2.0x/ico_tabar_prefecture_pressed.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_account.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_homepage_clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_homepage_clock.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_homepage_jifen_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_homepage_jifen_btn.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_homepage_qiandao_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_homepage_qiandao_btn.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_homepage_xinfeng.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_homepage_xinfeng.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_kefu_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_kefu_btn.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_loading_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_loading_bg.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_login_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_login_bg.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_login_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_login_close.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_minepage_class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_minepage_class.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_minepage_item_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_minepage_item_arrow.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_minepage_jifen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_minepage_jifen.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_minepage_shezhi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_minepage_shezhi.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_minepage_shouchang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_minepage_shouchang.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_minepage_xiaoxi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_minepage_xiaoxi.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_minepage_yijian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_minepage_yijian.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_person_clerk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_person_clerk.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_person_doctor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_person_doctor.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_person_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_person_normal.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_person_nurse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_person_nurse.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_person_physician.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_person_physician.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_person_sales.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_person_sales.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_pwd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_pwd.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_qiandao_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_qiandao_bg.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_tabar_class_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_tabar_class_normal.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_tabar_class_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_tabar_class_pressed.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_tabar_home_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_tabar_home_normal.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_tabar_home_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_tabar_home_pressed.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_tabar_mine_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_tabar_mine_normal.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_tabar_mine_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_tabar_mine_pressed.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_tabar_prefecture_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_tabar_prefecture_normal.png -------------------------------------------------------------------------------- /assets/images/3.0x/ico_tabar_prefecture_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/3.0x/ico_tabar_prefecture_pressed.png -------------------------------------------------------------------------------- /assets/images/ic_arrow_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ic_arrow_right.png -------------------------------------------------------------------------------- /assets/images/ic_back_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ic_back_black.png -------------------------------------------------------------------------------- /assets/images/ico_account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_account.png -------------------------------------------------------------------------------- /assets/images/ico_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_back.png -------------------------------------------------------------------------------- /assets/images/ico_dengji_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_dengji_bg.png -------------------------------------------------------------------------------- /assets/images/ico_dengji_bg_short.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_dengji_bg_short.png -------------------------------------------------------------------------------- /assets/images/ico_homepage_clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_homepage_clock.png -------------------------------------------------------------------------------- /assets/images/ico_homepage_comment_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_homepage_comment_gray.png -------------------------------------------------------------------------------- /assets/images/ico_homepage_eye_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_homepage_eye_gray.png -------------------------------------------------------------------------------- /assets/images/ico_homepage_jifen_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_homepage_jifen_btn.png -------------------------------------------------------------------------------- /assets/images/ico_homepage_qiandao_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_homepage_qiandao_btn.png -------------------------------------------------------------------------------- /assets/images/ico_homepage_xinfeng.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_homepage_xinfeng.png -------------------------------------------------------------------------------- /assets/images/ico_kefu_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_kefu_btn.png -------------------------------------------------------------------------------- /assets/images/ico_loading_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_loading_bg.png -------------------------------------------------------------------------------- /assets/images/ico_login_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_login_bg.png -------------------------------------------------------------------------------- /assets/images/ico_login_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_login_close.png -------------------------------------------------------------------------------- /assets/images/ico_minepage_class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_minepage_class.png -------------------------------------------------------------------------------- /assets/images/ico_minepage_item_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_minepage_item_arrow.png -------------------------------------------------------------------------------- /assets/images/ico_minepage_jifen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_minepage_jifen.png -------------------------------------------------------------------------------- /assets/images/ico_minepage_shezhi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_minepage_shezhi.png -------------------------------------------------------------------------------- /assets/images/ico_minepage_shouchang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_minepage_shouchang.png -------------------------------------------------------------------------------- /assets/images/ico_minepage_xiaoxi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_minepage_xiaoxi.png -------------------------------------------------------------------------------- /assets/images/ico_minepage_yijian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_minepage_yijian.png -------------------------------------------------------------------------------- /assets/images/ico_modal_close_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_modal_close_btn.png -------------------------------------------------------------------------------- /assets/images/ico_person_clerk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_person_clerk.png -------------------------------------------------------------------------------- /assets/images/ico_person_doctor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_person_doctor.png -------------------------------------------------------------------------------- /assets/images/ico_person_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_person_normal.png -------------------------------------------------------------------------------- /assets/images/ico_person_nurse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_person_nurse.png -------------------------------------------------------------------------------- /assets/images/ico_person_physician.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_person_physician.png -------------------------------------------------------------------------------- /assets/images/ico_person_sales.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_person_sales.png -------------------------------------------------------------------------------- /assets/images/ico_pwd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_pwd.png -------------------------------------------------------------------------------- /assets/images/ico_qiandao_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_qiandao_bg.png -------------------------------------------------------------------------------- /assets/images/ico_qiandao_tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_qiandao_tag.png -------------------------------------------------------------------------------- /assets/images/ico_qiandao_yiqian_gou.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_qiandao_yiqian_gou.png -------------------------------------------------------------------------------- /assets/images/ico_sex_nan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_sex_nan.png -------------------------------------------------------------------------------- /assets/images/ico_sex_nv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_sex_nv.png -------------------------------------------------------------------------------- /assets/images/ico_tabar_class_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_tabar_class_normal.png -------------------------------------------------------------------------------- /assets/images/ico_tabar_class_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_tabar_class_pressed.png -------------------------------------------------------------------------------- /assets/images/ico_tabar_home_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_tabar_home_normal.png -------------------------------------------------------------------------------- /assets/images/ico_tabar_home_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_tabar_home_pressed.png -------------------------------------------------------------------------------- /assets/images/ico_tabar_mine_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_tabar_mine_normal.png -------------------------------------------------------------------------------- /assets/images/ico_tabar_mine_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_tabar_mine_pressed.png -------------------------------------------------------------------------------- /assets/images/ico_tabar_prefecture_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_tabar_prefecture_normal.png -------------------------------------------------------------------------------- /assets/images/ico_tabar_prefecture_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/ico_tabar_prefecture_pressed.png -------------------------------------------------------------------------------- /assets/images/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/loading.gif -------------------------------------------------------------------------------- /assets/images/wechat_friends.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/assets/images/wechat_friends.png -------------------------------------------------------------------------------- /flutter_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/flutter_1.png -------------------------------------------------------------------------------- /flutter_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/flutter_2.png -------------------------------------------------------------------------------- /flutter_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/flutter_3.png -------------------------------------------------------------------------------- /flutter_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/flutter_4.png -------------------------------------------------------------------------------- /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/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | generated_key_values = {} 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) do |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | generated_key_values[podname] = podpath 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | end 32 | generated_key_values 33 | end 34 | 35 | target 'Runner' do 36 | # Flutter Pod 37 | 38 | copied_flutter_dir = File.join(__dir__, 'Flutter') 39 | copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') 40 | copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') 41 | unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) 42 | # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. 43 | # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. 44 | # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. 45 | 46 | generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') 47 | unless File.exist?(generated_xcode_build_settings_path) 48 | raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" 49 | end 50 | generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) 51 | cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; 52 | 53 | unless File.exist?(copied_framework_path) 54 | FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) 55 | end 56 | unless File.exist?(copied_podspec_path) 57 | FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) 58 | end 59 | end 60 | 61 | # Keep pod path relative so it can be checked into Podfile.lock. 62 | pod 'Flutter', :path => 'Flutter' 63 | 64 | # Plugin Pods 65 | 66 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 67 | # referring to absolute paths on developers' machines. 68 | system('rm -rf .symlinks') 69 | system('mkdir -p .symlinks/plugins') 70 | plugin_pods = parse_KV_file('../.flutter-plugins') 71 | plugin_pods.each do |name, path| 72 | symlink = File.join('.symlinks', 'plugins', name) 73 | File.symlink(path, symlink) 74 | pod name, :path => File.join(symlink, 'ios') 75 | end 76 | end 77 | 78 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 79 | install! 'cocoapods', :disable_input_output_paths => true 80 | 81 | post_install do |installer| 82 | installer.pods_project.targets.each do |target| 83 | target.build_configurations.each do |config| 84 | config.build_settings['ENABLE_BITCODE'] = 'NO' 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - fluwx (0.0.1): 4 | - Flutter 5 | - FMDB (2.7.5): 6 | - FMDB/standard (= 2.7.5) 7 | - FMDB/standard (2.7.5) 8 | - path_provider (0.0.1): 9 | - Flutter 10 | - shared_preferences (0.0.1): 11 | - Flutter 12 | - shared_preferences_macos (0.0.1): 13 | - Flutter 14 | - shared_preferences_web (0.0.1): 15 | - Flutter 16 | - sqflite (0.0.1): 17 | - Flutter 18 | - FMDB (~> 2.7.2) 19 | - url_launcher (0.0.1): 20 | - Flutter 21 | - url_launcher_macos (0.0.1): 22 | - Flutter 23 | - url_launcher_web (0.0.1): 24 | - Flutter 25 | - webview_flutter (0.0.1): 26 | - Flutter 27 | 28 | DEPENDENCIES: 29 | - Flutter (from `Flutter`) 30 | - fluwx (from `.symlinks/plugins/fluwx/ios`) 31 | - path_provider (from `.symlinks/plugins/path_provider/ios`) 32 | - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) 33 | - shared_preferences_macos (from `.symlinks/plugins/shared_preferences_macos/ios`) 34 | - shared_preferences_web (from `.symlinks/plugins/shared_preferences_web/ios`) 35 | - sqflite (from `.symlinks/plugins/sqflite/ios`) 36 | - url_launcher (from `.symlinks/plugins/url_launcher/ios`) 37 | - url_launcher_macos (from `.symlinks/plugins/url_launcher_macos/ios`) 38 | - url_launcher_web (from `.symlinks/plugins/url_launcher_web/ios`) 39 | - webview_flutter (from `.symlinks/plugins/webview_flutter/ios`) 40 | 41 | SPEC REPOS: 42 | trunk: 43 | - FMDB 44 | 45 | EXTERNAL SOURCES: 46 | Flutter: 47 | :path: Flutter 48 | fluwx: 49 | :path: ".symlinks/plugins/fluwx/ios" 50 | path_provider: 51 | :path: ".symlinks/plugins/path_provider/ios" 52 | shared_preferences: 53 | :path: ".symlinks/plugins/shared_preferences/ios" 54 | shared_preferences_macos: 55 | :path: ".symlinks/plugins/shared_preferences_macos/ios" 56 | shared_preferences_web: 57 | :path: ".symlinks/plugins/shared_preferences_web/ios" 58 | sqflite: 59 | :path: ".symlinks/plugins/sqflite/ios" 60 | url_launcher: 61 | :path: ".symlinks/plugins/url_launcher/ios" 62 | url_launcher_macos: 63 | :path: ".symlinks/plugins/url_launcher_macos/ios" 64 | url_launcher_web: 65 | :path: ".symlinks/plugins/url_launcher_web/ios" 66 | webview_flutter: 67 | :path: ".symlinks/plugins/webview_flutter/ios" 68 | 69 | SPEC CHECKSUMS: 70 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec 71 | fluwx: e46c9ea0278bc6b4a2686dd29ec4ff4eb509e626 72 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a 73 | path_provider: fb74bd0465e96b594bb3b5088ee4a4e7bb1f2a9d 74 | shared_preferences: 430726339841afefe5142b9c1f50cb6bd7793e01 75 | shared_preferences_macos: f3f29b71ccbb56bf40c9dd6396c9acf15e214087 76 | shared_preferences_web: 141cce0c3ed1a1c5bf2a0e44f52d31eeb66e5ea9 77 | sqflite: 4001a31ff81d210346b500c55b17f4d6c7589dd0 78 | url_launcher: a1c0cc845906122c4784c542523d8cacbded5626 79 | url_launcher_macos: fd7894421cd39320dce5f292fc99ea9270b2a313 80 | url_launcher_web: e5527357f037c87560776e36436bf2b0288b965c 81 | webview_flutter: db3aba222b23e4dc432e0c5882834123dc50ff9f 82 | 83 | PODFILE CHECKSUM: 3dbe063e9c90a5d7c9e4e76e70a821b9e2c1d271 84 | 85 | COCOAPODS: 1.8.4 86 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /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.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | @property (nonatomic, strong) FlutterViewController* controller; 6 | @end 7 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | #import "GeneratedPluginRegistrant.h" 3 | #import "RsaAndBase64.h" 4 | 5 | @implementation AppDelegate 6 | 7 | - (BOOL)application:(UIApplication *)application 8 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 9 | [NSThread sleepForTimeInterval:1]; 10 | self.controller = (FlutterViewController*)self.window.rootViewController; 11 | [self configureFlutterEngine]; 12 | [GeneratedPluginRegistrant registerWithRegistry:self]; 13 | // Override point for customization after application launch. 14 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 15 | } 16 | 17 | 18 | - (void) configureFlutterEngine{ 19 | [self registerEncocd]; 20 | } 21 | 22 | 23 | //注册加密模块 24 | - (void)registerEncocd{ 25 | FlutterMethodChannel* encodeChannel = [FlutterMethodChannel 26 | methodChannelWithName:@"www.guigug.com/rsa_and_base64" 27 | binaryMessenger:self.controller ]; 28 | 29 | [encodeChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { 30 | if ([@"encode" isEqualToString:call.method]) { 31 | result([RsaAndBase64 rsaAndBase64Encode:call.arguments[@"plaint"]]); 32 | } else { 33 | result(FlutterMethodNotImplemented); 34 | } }]; 35 | } 36 | @end 37 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "icon-20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "icon-20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "idiom" : "iphone", 17 | "size" : "29x29", 18 | "scale" : "1x" 19 | }, 20 | { 21 | "size" : "29x29", 22 | "idiom" : "iphone", 23 | "filename" : "icon-29@2x.png", 24 | "scale" : "2x" 25 | }, 26 | { 27 | "size" : "29x29", 28 | "idiom" : "iphone", 29 | "filename" : "icon-29@3x.png", 30 | "scale" : "3x" 31 | }, 32 | { 33 | "size" : "40x40", 34 | "idiom" : "iphone", 35 | "filename" : "icon-40@2x.png", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "size" : "40x40", 40 | "idiom" : "iphone", 41 | "filename" : "icon-40@3x.png", 42 | "scale" : "3x" 43 | }, 44 | { 45 | "size" : "60x60", 46 | "idiom" : "iphone", 47 | "filename" : "icon-60@2x.png", 48 | "scale" : "2x" 49 | }, 50 | { 51 | "size" : "60x60", 52 | "idiom" : "iphone", 53 | "filename" : "icon-60@3x.png", 54 | "scale" : "3x" 55 | }, 56 | { 57 | "idiom" : "ipad", 58 | "size" : "20x20", 59 | "scale" : "1x" 60 | }, 61 | { 62 | "idiom" : "ipad", 63 | "size" : "20x20", 64 | "scale" : "2x" 65 | }, 66 | { 67 | "idiom" : "ipad", 68 | "size" : "29x29", 69 | "scale" : "1x" 70 | }, 71 | { 72 | "idiom" : "ipad", 73 | "size" : "29x29", 74 | "scale" : "2x" 75 | }, 76 | { 77 | "idiom" : "ipad", 78 | "size" : "40x40", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "idiom" : "ipad", 83 | "size" : "40x40", 84 | "scale" : "2x" 85 | }, 86 | { 87 | "idiom" : "ipad", 88 | "size" : "76x76", 89 | "scale" : "1x" 90 | }, 91 | { 92 | "idiom" : "ipad", 93 | "size" : "76x76", 94 | "scale" : "2x" 95 | }, 96 | { 97 | "idiom" : "ipad", 98 | "size" : "83.5x83.5", 99 | "scale" : "2x" 100 | }, 101 | { 102 | "size" : "1024x1024", 103 | "idiom" : "ios-marketing", 104 | "filename" : "icon-1024.png", 105 | "scale" : "1x" 106 | } 107 | ], 108 | "info" : { 109 | "version" : 1, 110 | "author" : "xcode" 111 | } 112 | } -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-1024.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "splash_logo_one.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "splash_logo_one@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "splash_logo_one@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /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/Assets.xcassets/LaunchImage.imageset/splash_logo_one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/ios/Runner/Assets.xcassets/LaunchImage.imageset/splash_logo_one.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/splash_logo_one@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/ios/Runner/Assets.xcassets/LaunchImage.imageset/splash_logo_one@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/splash_logo_one@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/ios/Runner/Assets.xcassets/LaunchImage.imageset/splash_logo_one@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/splash_logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "splash_logo.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "splash_logo@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "splash_logo@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/splash_logo.imageset/splash_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/ios/Runner/Assets.xcassets/splash_logo.imageset/splash_logo.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/splash_logo.imageset/splash_logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/ios/Runner/Assets.xcassets/splash_logo.imageset/splash_logo@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/splash_logo.imageset/splash_logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bozaigao/fish-redux-demo/da8dd7fa2d9698e47933d73273b9c8f5ea569fc7/ios/Runner/Assets.xcassets/splash_logo.imageset/splash_logo@3x.png -------------------------------------------------------------------------------- /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 | 医点数据 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 | NSAppTransportSecurity 45 | 46 | NSAllowsArbitraryLoads 47 | 48 | NSExceptionDomains 49 | 50 | localhost 51 | 52 | NSExceptionAllowsInsecureHTTPLoads 53 | 54 | 55 | 56 | 57 | io.flutter.embedded_views_preview 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /ios/Runner/RsaAndBase64/RSAEncryptor.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface RSAEncryptor : NSObject 4 | 5 | /** 6 | * 加密方法 7 | * 8 | * @param str 需要加密的字符串 9 | * @param path '.der'格式的公钥文件路径 10 | */ 11 | + (NSString *)encryptString:(NSString *)str publicKeyWithContentsOfFile:(NSString *)path; 12 | 13 | /** 14 | * 解密方法 15 | * 16 | * @param str 需要解密的字符串 17 | * @param path '.p12'格式的私钥文件路径 18 | * @param password 私钥文件密码 19 | */ 20 | + (NSString *)decryptString:(NSString *)str privateKeyWithContentsOfFile:(NSString *)path password:(NSString *)password; 21 | 22 | /** 23 | * 加密方法 24 | * 25 | * @param str 需要加密的字符串 26 | * @param pubKey 公钥字符串 27 | */ 28 | + (NSString *)encryptString:(NSString *)str publicKey:(NSString *)pubKey; 29 | 30 | /** 31 | * 解密方法 32 | * 33 | * @param str 需要解密的字符串 34 | * @param privKey 私钥字符串 35 | */ 36 | + (NSString *)decryptString:(NSString *)str privateKey:(NSString *)privKey; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /ios/Runner/RsaAndBase64/RsaAndBase64.h: -------------------------------------------------------------------------------- 1 | // 2 | // RsaAndBase64.h 3 | // Mobispot 4 | // 5 | // Created by bozaigao on 2017/6/21. 6 | // Copyright © 2017年 Facebook. All rights reserved. 7 | // 8 | #import 9 | @interface RsaAndBase64 : NSObject 10 | + (NSString*) rsaAndBase64Encode:(NSString *) plaint; 11 | @end 12 | -------------------------------------------------------------------------------- /ios/Runner/RsaAndBase64/RsaAndBase64.m: -------------------------------------------------------------------------------- 1 | // 2 | // RsaAndBase64.m 3 | // Mobispot 4 | // RsaAndBase64加密 5 | // Created by bozaigao on 2017/6/21. 6 | // Copyright © 2017年 Facebook. All rights reserved. 7 | #import "RsaAndBase64.h" 8 | #import "RSAEncryptor.h" 9 | //处于业务安全考虑,该公钥已被隐藏 10 | #define public_key @"xxx" 11 | @implementation RsaAndBase64 12 | 13 | /** 14 | *调用原生模块进行rsa加密和base64编码 15 | */ 16 | + (NSString*) rsaAndBase64Encode:(NSString *) plaint 17 | { 18 | NSString *encryptStr = [RSAEncryptor encryptString:plaint publicKey:public_key]; 19 | NSLog(@"加密后的数据为: %@", encryptStr); 20 | return encryptStr; 21 | } 22 | 23 | /** 24 | *调用原生模块base64解码 25 | */ 26 | + (NSString*) decryptByPublicKey:(NSString *) plaint 27 | { 28 | NSData *plaint_data = 29 | [plaint dataUsingEncoding:NSUTF8StringEncoding]; 30 | NSData *nsdataDecoded = [plaint_data initWithBase64EncodedData:plaint_data options:0]; 31 | NSString *str = [[NSString alloc] initWithData:nsdataDecoded encoding:NSUTF8StringEncoding]; 32 | NSLog(@"解密后的数据为: %@", str); 33 | return str; 34 | } 35 | @end 36 | -------------------------------------------------------------------------------- /ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/app.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:fluro/fluro.dart'; 3 | import 'package:flutter/material.dart' hide Action; 4 | import 'package:flutter_app/routes/index/page.dart'; 5 | import 'package:flutter_app/utils/log_utils.dart'; 6 | import 'package:flutter_app/utils/routes/application.dart'; 7 | import 'package:flutter_app/utils/routes/routers.dart'; 8 | import 'package:oktoast/oktoast.dart'; 9 | import 'global_store/state.dart'; 10 | import 'global_store/store.dart'; 11 | 12 | /// 创建应用的根 Widget 13 | /// 1. 创建一个简单的路由,并注册页面 14 | /// 2. 对所需的页面进行和 AppStore 的连接 15 | /// 3. 对所需的页面进行 AOP 的增强 16 | Widget createApp() { 17 | final router = Router(); 18 | Routes.configureRoutes(router); 19 | Application.router = router; 20 | Log.init(); 21 | 22 | dynamic indexPage = IndexPage(); 23 | indexPage.connectExtraStore(GlobalStore.store, 24 | (Object pagestate, GlobalState appState) { 25 | final GlobalBaseState p = pagestate; 26 | if (p.userInfo != appState.userInfo) { 27 | if (pagestate is Cloneable) { 28 | final Object copy = pagestate.clone(); 29 | final GlobalBaseState newState = copy; 30 | newState.userInfo = appState.userInfo; 31 | return newState; 32 | } 33 | } 34 | return pagestate; 35 | }); 36 | 37 | return OKToast( 38 | child: MaterialApp( 39 | debugShowCheckedModeBanner: false, 40 | home: indexPage.buildPage(null), 41 | onGenerateRoute: Application.router.generator, 42 | ), 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /lib/global_store/action.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter_app/models/user_info_entity.dart'; 3 | 4 | enum GlobalAction { updateUserInfo } 5 | 6 | class GlobalActionCreator { 7 | static Action updateUserInfo(UserInfoEntity userInfo) { 8 | return Action(GlobalAction.updateUserInfo, payload: userInfo); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/global_store/reducer.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'action.dart'; 3 | import 'state.dart'; 4 | 5 | Reducer buildReducer() { 6 | return asReducer( 7 | >{ 8 | GlobalAction.updateUserInfo: updateUserInfo, 9 | }, 10 | ); 11 | } 12 | 13 | GlobalState updateUserInfo(GlobalState state, Action action) { 14 | return state.clone()..userInfo = action.payload; 15 | } 16 | -------------------------------------------------------------------------------- /lib/global_store/state.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter_app/models/user_info_entity.dart'; 3 | 4 | abstract class GlobalBaseState { 5 | UserInfoEntity get userInfo; 6 | set userInfo(UserInfoEntity userInfo); 7 | } 8 | 9 | class GlobalState implements GlobalBaseState, Cloneable { 10 | @override 11 | UserInfoEntity userInfo; 12 | 13 | @override 14 | GlobalState clone() { 15 | return GlobalState(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/global_store/store.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'reducer.dart'; 3 | import 'state.dart'; 4 | 5 | /// 建立一个AppStore 6 | /// 目前它的功能只有切换主题 7 | class GlobalStore { 8 | static Store _globalStore; 9 | 10 | static Store get store => 11 | _globalStore ??= createStore(GlobalState(), buildReducer()); 12 | } 13 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'app.dart'; 4 | 5 | void main(){ 6 | runApp(createApp()); 7 | } -------------------------------------------------------------------------------- /lib/models/entity_factory.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_app/models/user_info_entity.dart'; 2 | 3 | import 'home/material_entity.dart'; 4 | import 'home/plate_entity.dart'; 5 | import 'mine/collect_entity.dart'; 6 | 7 | class EntityFactory { 8 | static T generateOBJ(json) { 9 | if (T.toString() == "PlateEntity") { 10 | return PlateEntity.fromJson(json) as T; 11 | } else if (T.toString() == "UserInfoEntity") { 12 | return UserInfoEntity.fromJson(json) as T; 13 | }else if (T.toString() == "CollectEntity") { 14 | return CollectEntity.fromJson(json) as T; 15 | }else if (T.toString() == "MaterialEntity") { 16 | return MaterialEntity.fromJson(json) as T; 17 | } else { 18 | return null; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/models/home/material_entity.dart: -------------------------------------------------------------------------------- 1 | class MaterialEntity { 2 | String content; 3 | String crop; 4 | DoctorEntity doctor; 5 | String doctorId; 6 | bool hasCollect; 7 | String icon; 8 | String id; 9 | String origin; 10 | String title; 11 | num view; 12 | 13 | MaterialEntity( 14 | {this.content, 15 | this.crop, 16 | this.doctor, 17 | this.doctorId, 18 | this.hasCollect, 19 | this.icon, 20 | this.id, 21 | this.origin, 22 | this.title, 23 | this.view}); 24 | 25 | MaterialEntity.fromJson(Map json) 26 | : content = json['content'], 27 | crop = json['crop'], 28 | doctorId = json['doctorId'], 29 | hasCollect = json['hasCollect'], 30 | icon = json['icon'], 31 | id = json['id'], 32 | origin = json['origin'], 33 | title = json['title'], 34 | view = json['view'], 35 | doctor = DoctorEntity.fromJson(json['doctor']); 36 | 37 | Map toJson() => { 38 | 'content': content, 39 | 'crop': crop, 40 | 'doctorId': doctorId, 41 | 'hasCollect': hasCollect, 42 | 'icon': icon, 43 | 'id': id, 44 | 'origin': origin, 45 | 'title': title, 46 | 'view': view, 47 | 'doctor': doctor?.toJson() 48 | }; 49 | } 50 | 51 | class DoctorEntity { 52 | String accountId; 53 | String epartmene; 54 | String gender; 55 | String hospital; 56 | String mobile; 57 | String name; 58 | String post; 59 | 60 | DoctorEntity( 61 | {this.accountId, 62 | this.epartmene, 63 | this.gender, 64 | this.hospital, 65 | this.mobile, 66 | this.name, 67 | this.post}); 68 | 69 | DoctorEntity.fromJson(Map json) { 70 | if (json != null) { 71 | accountId = json['accountId']; 72 | epartmene = json['epartmene']; 73 | gender = json['gender']; 74 | hospital = json['hospital']; 75 | mobile = json['mobile']; 76 | name = json['name']; 77 | post = json['post']; 78 | } 79 | } 80 | 81 | Map toJson() => { 82 | 'accountId': accountId, 83 | 'epartmene': epartmene, 84 | 'gender': gender, 85 | 'hospital': hospital, 86 | 'mobile': mobile, 87 | 'name': name, 88 | 'post': post 89 | }; 90 | } 91 | -------------------------------------------------------------------------------- /lib/models/home/plate_entity.dart: -------------------------------------------------------------------------------- 1 | class PlateEntity { 2 | int id; 3 | String header; 4 | int uiType; 5 | List items; 6 | 7 | PlateEntity({this.id, this.header, this.uiType, this.items}); 8 | 9 | PlateEntity.fromJson(Map json) { 10 | id = json['id']; 11 | header = json['header']; 12 | uiType = json['uiType']; 13 | if (json['items'] != null) { 14 | items = new List(); 15 | (json['items'] as List).forEach((v) { 16 | items.add(new PlateItem.fromJson(v)); 17 | }); 18 | } 19 | } 20 | 21 | Map toJson() { 22 | final Map data = new Map(); 23 | data['id'] = this.id; 24 | data['header'] = this.header; 25 | data['uiType'] = this.uiType; 26 | if (this.items != null) { 27 | data['items'] = this.items.map((v) => v.toJson()).toList(); 28 | } 29 | return data; 30 | } 31 | } 32 | 33 | class PlateItem { 34 | int id; 35 | int plateId; 36 | String goId; 37 | String icon1; 38 | String icon2; 39 | String line1; 40 | String line2; 41 | String line3; 42 | int goType; 43 | 44 | PlateItem( 45 | {this.id, 46 | this.plateId, 47 | this.goId, 48 | this.icon1, 49 | this.icon2, 50 | this.line1, 51 | this.line2, 52 | this.line3, 53 | this.goType}); 54 | 55 | PlateItem.fromJson(Map json) { 56 | id = json['id']; 57 | plateId = json['plateId']; 58 | goId = json['goId']; 59 | icon1 = json['icon1']; 60 | icon2 = json['icon2']; 61 | line1 = json['line1']; 62 | line2 = json['line2']; 63 | line3 = json['line3']; 64 | goType = json['goType']; 65 | } 66 | 67 | Map toJson() { 68 | final Map data = new Map(); 69 | data['id'] = this.id; 70 | data['plateId'] = this.plateId; 71 | data['goId'] = this.goId; 72 | data['icon1'] = this.icon1; 73 | data['icon2'] = this.icon2; 74 | data['line1'] = this.line1; 75 | data['line2'] = this.line2; 76 | data['line3'] = this.line3; 77 | data['goType'] = this.goType; 78 | return data; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /lib/models/index/page_component.dart: -------------------------------------------------------------------------------- 1 | class PageComponent { 2 | const PageComponent({this.pageName, this.page}); 3 | 4 | final String pageName; 5 | final dynamic page; 6 | } -------------------------------------------------------------------------------- /lib/models/mine/collect_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_app/models/home/plate_entity.dart'; 2 | 3 | class CollectEntity { 4 | num first; 5 | num pageNo; 6 | num pageSize; 7 | List result; 8 | num total; 9 | num totalPage; 10 | CollectEntity({ 11 | this.first, 12 | this.pageNo, 13 | this.pageSize, 14 | this.result, 15 | this.total, 16 | this.totalPage 17 | }); 18 | 19 | CollectEntity.fromJson(Map < String, dynamic > json): 20 | first=json['first'], 21 | pageNo=json['pageNo'], 22 | pageSize=json['pageSize'], 23 | total=json['total'], 24 | totalPage=json['totalPage'], 25 | result=(json['result'] as List)?.map((l)=>PlateItem.fromJson(l))?.toList(); 26 | Map toJson() => { 27 | 'first':first, 28 | 'pageNo':pageNo, 29 | 'pageSize':pageSize, 30 | 'total':total, 31 | 'totalPage':totalPage, 32 | 'result':result?.map((it)=>it.toJson())?.toList() 33 | }; 34 | } -------------------------------------------------------------------------------- /lib/res/colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class MyColors { 4 | static const Color app_main = Color(0xFF34B0B0); 5 | static const Color themeColor = Color(0xFF34B0B0); 6 | static const Color dark_app_main = Color(0xFF34B0B0); 7 | 8 | static const Color bg_color = Color(0xfff1f1f1); 9 | static const Color dark_bg_color = Color(0xFF18191A); 10 | 11 | static const Color material_bg = Color(0xFFFFFFFF); 12 | static const Color dark_material_bg = Color(0xFF303233); 13 | 14 | static const Color text = Color(0xFF333333); 15 | static const Color dark_text = Color(0xFFB8B8B8); 16 | 17 | static const Color text_gray = Color(0xFF999999); 18 | static const Color dark_text_gray = Color(0xFF666666); 19 | 20 | static const Color text_gray_c = Color(0xFFcccccc); 21 | static const Color dark_button_text = Color(0xFFF2F2F2); 22 | 23 | static const Color bg_gray = Color(0xFFF6F6F6); 24 | static const Color dark_bg_gray = Color(0xFF1F1F1F); 25 | 26 | static const Color line = Color(0xFFEEEEEE); 27 | static const Color dark_line = Color(0xFF3A3C3D); 28 | 29 | static const Color red = Color(0xFFFF4759); 30 | static const Color dark_red = Color(0xFFE03E4E); 31 | 32 | static const Color text_disabled = Color(0xFFD4E2FA); 33 | static const Color dark_text_disabled = Color(0xFFCEDBF2); 34 | 35 | static const Color button_disabled = Color(0xFF96BBFA); 36 | static const Color dark_button_disabled = Color(0xFF83A5E0); 37 | 38 | static const Color unselected_item_color = Color(0xff9b9b9b); 39 | static const Color dark_unselected_item_color = Color(0xFF4D4D4D); 40 | 41 | static const Color bg_gray_ = Color(0xFFFAFAFA); 42 | static const Color dark_bg_gray_ = Color(0xFF242526); 43 | 44 | static const Color pageDefaultBackgroundColor = Color(0xFFF8F8F8); 45 | static const Color textBlackColor = Color(0xFF182222); 46 | static const Color textGrayColorThree = Color(0xFF575757); 47 | static const Color lightBlackColor = Color(0xFF203737); 48 | static const Color statusBarColor = Color(0xFF60d7cb); 49 | static const Color textGrayColor = Color(0xFF979aa0); 50 | } 51 | -------------------------------------------------------------------------------- /lib/res/dimens.dart: -------------------------------------------------------------------------------- 1 | class Dimens { 2 | static const double font_sp10 = 10.0; 3 | static const double font_sp12 = 12.0; 4 | static const double font_sp14 = 14.0; 5 | static const double font_sp15 = 15.0; 6 | static const double font_sp16 = 16.0; 7 | static const double font_sp18 = 18.0; 8 | 9 | static const double gap_dp5 = 5; 10 | static const double gap_dp10 = 10; 11 | static const double gap_dp12 = 12; 12 | static const double gap_dp15 = 15; 13 | static const double gap_dp16 = 16; 14 | static const double gap_dp50 = 50; 15 | } 16 | -------------------------------------------------------------------------------- /lib/res/gaps.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | import 'dimens.dart'; 4 | 5 | /// 间隔 6 | class Gaps { 7 | /// 水平间隔 8 | static const Widget hGap4 = const SizedBox(width: 4.0); 9 | static const Widget hGap5 = const SizedBox(width: Dimens.gap_dp5); 10 | static const Widget hGap8 = const SizedBox(width: 8.0); 11 | static const Widget hGap10 = const SizedBox(width: Dimens.gap_dp10); 12 | static const Widget hGap12 = const SizedBox(width: 12.0); 13 | static const Widget hGap15 = const SizedBox(width: Dimens.gap_dp15); 14 | static const Widget hGap16 = const SizedBox(width: Dimens.gap_dp16); 15 | /// 垂直间隔 16 | static const Widget vGap4 = const SizedBox(height: 4.0); 17 | static const Widget vGap5 = const SizedBox(height: Dimens.gap_dp5); 18 | static const Widget vGap8 = const SizedBox(height: 8.0); 19 | static const Widget vGap10 = const SizedBox(height: Dimens.gap_dp10); 20 | static const Widget vGap12 = const SizedBox(height: 12.0); 21 | static const Widget vGap15 = const SizedBox(height: Dimens.gap_dp15); 22 | static const Widget vGap16 = const SizedBox(height: Dimens.gap_dp16); 23 | static const Widget vGap50 = const SizedBox(height: Dimens.gap_dp50); 24 | 25 | // static Widget line = const SizedBox( 26 | // height: 0.6, 27 | // width: double.infinity, 28 | // child: const DecoratedBox(decoration: BoxDecoration(color: MyColors.line)), 29 | // ); 30 | 31 | static Widget line = const Divider(); 32 | 33 | static Widget vLine = const SizedBox( 34 | width: 0.6, 35 | height: 24.0, 36 | child: const VerticalDivider(), 37 | ); 38 | 39 | static const Widget empty = const SizedBox(); 40 | } 41 | -------------------------------------------------------------------------------- /lib/res/resources.dart: -------------------------------------------------------------------------------- 1 | export 'colors.dart'; 2 | export 'dimens.dart'; 3 | export 'styles.dart'; 4 | export 'gaps.dart'; 5 | 6 | import 'package:flutter/widgets.dart'; 7 | import 'package:flutter_app/widgets/load_image.dart'; 8 | 9 | class Images { 10 | static const Widget arrowRight = const LoadAssetImage("ic_arrow_right", height: 16.0, width: 16.0); 11 | 12 | } -------------------------------------------------------------------------------- /lib/res/styles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | import 'colors.dart'; 4 | import 'dimens.dart'; 5 | 6 | class TextStyles { 7 | 8 | static const TextStyle textSize12 = const TextStyle( 9 | fontSize: Dimens.font_sp12, 10 | ); 11 | static const TextStyle textSize16 = const TextStyle( 12 | fontSize: Dimens.font_sp16, 13 | ); 14 | static const TextStyle textBold14 = const TextStyle( 15 | fontSize: Dimens.font_sp14, 16 | fontWeight: FontWeight.bold 17 | ); 18 | static const TextStyle textBold16 = const TextStyle( 19 | fontSize: Dimens.font_sp16, 20 | fontWeight: FontWeight.bold 21 | ); 22 | static const TextStyle textBold18 = const TextStyle( 23 | fontSize: Dimens.font_sp18, 24 | fontWeight: FontWeight.bold 25 | ); 26 | static const TextStyle textBold24 = const TextStyle( 27 | fontSize: 24.0, 28 | fontWeight: FontWeight.bold 29 | ); 30 | static const TextStyle textBold26 = const TextStyle( 31 | fontSize: 26.0, 32 | fontWeight: FontWeight.bold 33 | ); 34 | 35 | static const TextStyle textGray14 = const TextStyle( 36 | fontSize: Dimens.font_sp14, 37 | color: MyColors.text_gray, 38 | ); 39 | static const TextStyle textDarkGray14 = const TextStyle( 40 | fontSize: Dimens.font_sp14, 41 | color: MyColors.dark_text_gray, 42 | ); 43 | 44 | static const TextStyle textWhite14 = const TextStyle( 45 | fontSize: Dimens.font_sp14, 46 | color: Colors.white, 47 | ); 48 | 49 | static const TextStyle text = const TextStyle( 50 | fontSize: Dimens.font_sp14, 51 | color: MyColors.text, 52 | // https://github.com/flutter/flutter/issues/40248 53 | textBaseline: TextBaseline.alphabetic 54 | ); 55 | static const TextStyle textDark = const TextStyle( 56 | fontSize: Dimens.font_sp14, 57 | color: MyColors.dark_text, 58 | textBaseline: TextBaseline.alphabetic 59 | ); 60 | 61 | static const TextStyle textGray12 = const TextStyle( 62 | fontSize: Dimens.font_sp12, 63 | color: MyColors.text_gray, 64 | fontWeight: FontWeight.normal 65 | ); 66 | static const TextStyle textDarkGray12 = const TextStyle( 67 | fontSize: Dimens.font_sp12, 68 | color: MyColors.dark_text_gray, 69 | fontWeight: FontWeight.normal 70 | ); 71 | 72 | static const TextStyle textHint14 = const TextStyle( 73 | fontSize: Dimens.font_sp14, 74 | color: MyColors.dark_unselected_item_color 75 | ); 76 | } 77 | -------------------------------------------------------------------------------- /lib/routes/class_module/class_page/action.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | //TODO replace with your own action 4 | enum ClassAction { onRefresh } 5 | 6 | class ClassActionCreator { 7 | static Action onRefresh() { 8 | return const Action(ClassAction.onRefresh); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /lib/routes/class_module/class_page/effect.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flustars/flustars.dart'; 3 | import 'package:flutter_app/models/home/plate_entity.dart'; 4 | import 'package:flutter_app/routes/index/action.dart'; 5 | import 'package:flutter_app/utils/common.dart'; 6 | import 'package:flutter_app/utils/net/dio_utils.dart'; 7 | import 'package:flutter_app/utils/net/http_api.dart'; 8 | import 'package:flutter_app/utils/toast.dart'; 9 | import 'action.dart'; 10 | import 'state.dart'; 11 | 12 | Effect buildEffect() { 13 | return combineEffects(>{ 14 | Lifecycle.initState: _init, 15 | ClassAction.onRefresh: _init 16 | }); 17 | } 18 | 19 | Future _init(Action action, Context ctx) async { 20 | await DioUtils.instance.requestNetwork( 21 | Method.get, HttpApi.cloudAcademy, 22 | params: null, 23 | queryParameters: null, 24 | isList: true, onSuccessList: (list) async { 25 | await SpUtil.putObjectList(Constant.classData, list); 26 | ctx.dispatch(IndexActionCreator.initClassData(list)); 27 | }, onError: (code, msg) { 28 | Toast.show(msg); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /lib/routes/class_module/class_page/page.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | import 'effect.dart'; 4 | import 'reducer.dart'; 5 | import 'state.dart'; 6 | import 'view.dart'; 7 | 8 | class ClassPage extends Page>{ 9 | ClassPage() 10 | : super( 11 | initState: initState, 12 | effect: buildEffect(), 13 | reducer: buildReducer(), 14 | view: buildView, 15 | dependencies: Dependencies( 16 | adapter: null, 17 | slots: >{ 18 | }), 19 | middleware: >[ 20 | ],); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /lib/routes/class_module/class_page/reducer.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | import 'action.dart'; 4 | import 'state.dart'; 5 | 6 | Reducer buildReducer() { 7 | return asReducer( 8 | >{ 9 | }, 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /lib/routes/class_module/class_page/state.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter_app/models/home/plate_entity.dart'; 3 | 4 | class ClassState implements Cloneable { 5 | List plateList; 6 | @override 7 | ClassState clone() { 8 | return ClassState() 9 | ..plateList = plateList; 10 | } 11 | } 12 | 13 | ClassState initState(Map args) { 14 | return ClassState() 15 | ..plateList = []; 16 | } 17 | -------------------------------------------------------------------------------- /lib/routes/home_module/home_page/action.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | //TODO replace with your own action 4 | enum HomeAction { onRefresh } 5 | 6 | class HomeActionCreator { 7 | static Action onRefresh() { 8 | return const Action(HomeAction.onRefresh); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/routes/home_module/home_page/effect.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flustars/flustars.dart'; 3 | import 'package:flutter_app/models/home/plate_entity.dart'; 4 | import 'package:flutter_app/routes/index/action.dart'; 5 | import 'package:flutter_app/utils/common.dart'; 6 | import 'package:flutter_app/utils/net/dio_utils.dart'; 7 | import 'package:flutter_app/utils/net/http_api.dart'; 8 | import 'package:flutter_app/utils/toast.dart'; 9 | 10 | import 'action.dart'; 11 | import 'state.dart'; 12 | 13 | Effect buildEffect() { 14 | return combineEffects(>{ 15 | Lifecycle.initState: _init, 16 | HomeAction.onRefresh: _init 17 | }); 18 | } 19 | 20 | Future _init(Action action, Context ctx) async { 21 | await DioUtils.instance.requestNetwork( 22 | Method.get, HttpApi.getHomePagePlate, 23 | params: null, 24 | queryParameters: null, 25 | isList: true, onSuccessList: (list) async { 26 | await SpUtil.putObjectList(Constant.homeData, list); 27 | ctx.dispatch(IndexActionCreator.initHomeData(list)); 28 | }, onError: (code, msg) { 29 | Toast.show(msg); 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /lib/routes/home_module/home_page/page.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | import 'effect.dart'; 4 | import 'reducer.dart'; 5 | import 'state.dart'; 6 | import 'view.dart'; 7 | 8 | class HomePage extends Page> { 9 | HomePage() 10 | : super( 11 | initState: initState, 12 | effect: buildEffect(), 13 | reducer: buildReducer(), 14 | view: buildView, 15 | dependencies: Dependencies( 16 | adapter: null, 17 | slots: >{ 18 | }), 19 | middleware: >[ 20 | ],); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /lib/routes/home_module/home_page/reducer.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'state.dart'; 3 | 4 | Reducer buildReducer() { 5 | return asReducer( 6 | >{}, 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /lib/routes/home_module/home_page/state.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter_app/global_store/state.dart'; 3 | import 'package:flutter_app/models/home/plate_entity.dart'; 4 | import 'package:flutter_app/models/user_info_entity.dart'; 5 | 6 | class HomeState implements GlobalBaseState,Cloneable { 7 | List plateList; 8 | @override 9 | HomeState clone() { 10 | return HomeState() 11 | ..plateList = plateList 12 | ..userInfo = userInfo; 13 | } 14 | 15 | @override 16 | UserInfoEntity userInfo; 17 | } 18 | 19 | HomeState initState(Map args) { 20 | return HomeState() 21 | ..plateList = []; 22 | } 23 | -------------------------------------------------------------------------------- /lib/routes/home_module/home_page/widget/home_page_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_app/models/home/plate_entity.dart'; 4 | import 'package:flutter_app/res/colors.dart'; 5 | import 'package:flutter_app/utils/routes/fluro_navigator.dart'; 6 | import 'package:flutter_app/utils/routes/routers.dart'; 7 | import 'package:flutter_app/utils/screen_util.dart'; 8 | import 'package:flutter_app/utils/toast.dart'; 9 | 10 | import '../../../../widgets/load_image.dart'; 11 | 12 | class HomePageItem extends StatelessWidget { 13 | const HomePageItem({ 14 | Key key, 15 | @required this.item, 16 | }) : super(key: key); 17 | 18 | final PlateItem item; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | // TODO: implement build 23 | num width = ScreenUtil.screenWidthDp / 2; 24 | 25 | return Container( 26 | width: ScreenUtil.screenWidthDp / 2 - 28, 27 | margin: EdgeInsets.only(top: 15, left: 10), 28 | child: FlatButton( 29 | onPressed: () { 30 | if (item.goType == 4) { 31 | NavigatorUtils.push(context, 32 | '${Routes.newsDetailPage}?title=${Uri.encodeComponent(item.line1)}&id=${item.goId}'); 33 | //以下是包含参数回调 34 | // NavigatorUtils.pushResult(context, 35 | // '${Routes.newsDetailPage}?title=${Uri.encodeComponent(item.line1)}&id=${item.id}', 36 | // (result) { 37 | // print(result); 38 | // }); 39 | } else { 40 | Toast.show('学习视频还未开发'); 41 | } 42 | }, 43 | padding: EdgeInsets.all(0), 44 | child: Column( 45 | mainAxisAlignment: MainAxisAlignment.center, 46 | children: [ 47 | Container( 48 | width: width, 49 | height: scaleSize(90), 50 | decoration: BoxDecoration( 51 | borderRadius: BorderRadius.circular(scaleSize(4)), 52 | image: DecorationImage( 53 | image: CachedNetworkImageProvider(item.icon1), 54 | fit: BoxFit.cover, 55 | )), 56 | ), 57 | Container( 58 | width: width, 59 | margin: EdgeInsets.only( 60 | top: scaleSize(5), 61 | ), 62 | child: Text( 63 | item.line1, 64 | style: TextStyle( 65 | fontWeight: FontWeight.bold, 66 | fontSize: setSp(15), 67 | color: MyColors.lightBlackColor), 68 | maxLines: 1, 69 | overflow: TextOverflow.ellipsis, 70 | )), 71 | Padding( 72 | padding: 73 | EdgeInsets.only(left: scaleSize(8), right: scaleSize(8)), 74 | child: Container( 75 | margin: EdgeInsets.only(top: 5), 76 | child: Row( 77 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 78 | children: [ 79 | Row( 80 | crossAxisAlignment: CrossAxisAlignment.center, 81 | children: [ 82 | LoadImage( 83 | "ico_homepage_eye_gray", 84 | width: scaleSize(10), 85 | height: scaleSize(7), 86 | ), 87 | Container( 88 | margin: EdgeInsets.only(left: 2), 89 | child: Text( 90 | item.line2, 91 | style: TextStyle( 92 | color: MyColors.textGrayColorThree, 93 | fontSize: setSp(10)), 94 | )) 95 | ], 96 | ), 97 | Row( 98 | crossAxisAlignment: CrossAxisAlignment.center, 99 | children: [ 100 | LoadImage( 101 | "ico_homepage_comment_gray", 102 | width: scaleSize(9), 103 | height: scaleSize(9), 104 | ), 105 | Container( 106 | margin: EdgeInsets.only(left: 2), 107 | child: Text( 108 | item.line3, 109 | style: TextStyle( 110 | color: MyColors.textGrayColorThree, 111 | fontSize: setSp(10)), 112 | )) 113 | ], 114 | ) 115 | ], 116 | ))) 117 | ], 118 | ), 119 | ), 120 | ); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/routes/home_module/news_detail/action.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter_app/models/home/material_entity.dart'; 3 | 4 | //TODO replace with your own action 5 | enum NewsDetailAction { initData, updateWebViewHeight } 6 | 7 | class NewsDetailActionCreator { 8 | static Action initData(MaterialEntity materialEntity) { 9 | return Action(NewsDetailAction.initData, payload: materialEntity); 10 | } 11 | 12 | static Action updateWebViewHeight(double webViewHeight) { 13 | return Action(NewsDetailAction.updateWebViewHeight, payload: webViewHeight); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/routes/home_module/news_detail/effect.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter_app/models/home/material_entity.dart'; 3 | import 'package:flutter_app/routes/home_module/news_detail/action.dart'; 4 | import 'package:flutter_app/utils/net/dio_utils.dart'; 5 | import 'package:flutter_app/utils/net/http_api.dart'; 6 | import 'package:flutter_app/utils/toast.dart'; 7 | 8 | import 'state.dart'; 9 | 10 | Effect buildEffect() { 11 | return combineEffects(>{ 12 | Lifecycle.initState: _initData, 13 | }); 14 | } 15 | 16 | void _initData(Action action, Context ctx) { 17 | Map params = Map(); 18 | params["id"] = ctx.state.newsInfoModel.id; 19 | DioUtils.instance.requestNetwork( 20 | Method.get, HttpApi.getMaterialDetail, 21 | params: params, queryParameters: params, onSuccess: (data) async { 22 | ctx.dispatch(NewsDetailActionCreator.initData(data)); 23 | }, onError: (code, msg) { 24 | Toast.show(msg); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /lib/routes/home_module/news_detail/page.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | import 'effect.dart'; 4 | import 'reducer.dart'; 5 | import 'state.dart'; 6 | import 'view.dart'; 7 | 8 | class NewsDetailPage extends Page { 9 | NewsDetailPage() 10 | : super( 11 | initState: initState, 12 | effect: buildEffect(), 13 | reducer: buildReducer(), 14 | view: buildView, 15 | dependencies: Dependencies( 16 | adapter: null, 17 | slots: >{ 18 | }), 19 | middleware: >[ 20 | ],); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /lib/routes/home_module/news_detail/reducer.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | import 'action.dart'; 4 | import 'state.dart'; 5 | 6 | Reducer buildReducer() { 7 | return asReducer( 8 | >{ 9 | NewsDetailAction.initData: _initData, 10 | NewsDetailAction.updateWebViewHeight: _updateWebViewHeight, 11 | }, 12 | ); 13 | } 14 | 15 | NewsDetailState _initData(NewsDetailState state, Action action) { 16 | return state.clone() 17 | ..materialEntity = action.payload; 18 | } 19 | 20 | NewsDetailState _updateWebViewHeight(NewsDetailState state, Action action) { 21 | return state.clone() 22 | ..webViewHeight = action.payload; 23 | } 24 | -------------------------------------------------------------------------------- /lib/routes/home_module/news_detail/state.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter_app/models/home/material_entity.dart'; 3 | import 'package:flutter_app/utils/screen_util.dart'; 4 | 5 | class NewsDetailState implements Cloneable { 6 | NewsInfoModel newsInfoModel; 7 | MaterialEntity materialEntity; 8 | double webViewHeight; 9 | 10 | @override 11 | NewsDetailState clone() { 12 | return NewsDetailState() 13 | ..newsInfoModel = newsInfoModel 14 | ..materialEntity = materialEntity 15 | ..webViewHeight = webViewHeight; 16 | } 17 | } 18 | 19 | NewsDetailState initState(NewsInfoModel newsInfoModel) { 20 | return NewsDetailState() 21 | ..newsInfoModel = newsInfoModel 22 | ..materialEntity = null 23 | ..webViewHeight = ScreenUtil.screenHeightDp; 24 | } 25 | 26 | class NewsInfoModel { 27 | final String id; 28 | final String title; 29 | 30 | const NewsInfoModel({this.id, this.title}); 31 | } 32 | -------------------------------------------------------------------------------- /lib/routes/home_module/news_detail/view.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:fish_redux/fish_redux.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_app/res/colors.dart'; 7 | import 'package:flutter_app/routes/home_module/news_detail/action.dart'; 8 | import 'package:flutter_app/utils/progress.dart'; 9 | import 'package:flutter_app/utils/screen_util.dart'; 10 | import 'package:flutter_app/widgets/top_header.dart'; 11 | import 'package:webview_flutter/webview_flutter.dart'; 12 | import 'state.dart'; 13 | 14 | Widget buildView( 15 | NewsDetailState state, Dispatch dispatch, ViewService viewService) { 16 | String webHtml = "" 17 | "" 18 | "" 19 | "" 20 | "" 21 | "" 22 | "" 23 | "" 54 | "" 55 | "" 56 | "${state.materialEntity != null ? state.materialEntity.content : ''}" 57 | "" 64 | "" 65 | ""; 66 | 67 | return Container( 68 | color: Colors.white, 69 | child: Stack( 70 | children: [ 71 | TopHeader( 72 | title: state.newsInfoModel.title, 73 | ), 74 | SafeArea( 75 | child: Container( 76 | margin: EdgeInsets.only(top: scaleSize(44)), 77 | child: Container( 78 | color: Colors.white, 79 | child: Column(children: [ 80 | Expanded(child: state.materialEntity != null 81 | ? WebView( 82 | initialUrl: new Uri.dataFromString(webHtml, 83 | mimeType: 'text/html', 84 | encoding: Encoding.getByName("utf-8")) 85 | .toString(), 86 | debuggingEnabled: false, 87 | javascriptMode: JavascriptMode.unrestricted, 88 | onPageStarted: (url) { 89 | showProgress(viewService.context); 90 | }, 91 | onPageFinished: (url) { 92 | hideProgress(viewService.context); 93 | }, 94 | javascriptChannels: [ 95 | JavascriptChannel( 96 | name: "sendHeight", 97 | onMessageReceived: 98 | (JavascriptMessage message) { 99 | println('高度' + message.message); 100 | dispatch(NewsDetailActionCreator 101 | .updateWebViewHeight(double.parse( 102 | message.message) + 103 | scaleSize(50))); 104 | }), 105 | ].toSet(), 106 | ):SizedBox(),) 107 | ],), 108 | ), 109 | ), 110 | ) 111 | ], 112 | ), 113 | ); 114 | } 115 | -------------------------------------------------------------------------------- /lib/routes/index/action.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter_app/models/home/plate_entity.dart'; 3 | 4 | //TODO replace with your own action 5 | enum IndexAction { 6 | onJump, 7 | jump, 8 | initPageList, 9 | initHomeData, 10 | initClassData, 11 | initPrefectureData, 12 | getUserInfo 13 | } 14 | 15 | class IndexActionCreator { 16 | static Action jump(int index) { 17 | return Action(IndexAction.jump, payload: index); 18 | } 19 | 20 | static Action initPageList(dynamic list) { 21 | return Action(IndexAction.initPageList, payload: list); 22 | } 23 | 24 | static Action initHomeData(List plateList) { 25 | return Action(IndexAction.initHomeData, payload: plateList); 26 | } 27 | 28 | static Action initClassData(List plateList) { 29 | return Action(IndexAction.initClassData, payload: plateList); 30 | } 31 | 32 | static Action initPrefectureData(List plateList) { 33 | return Action(IndexAction.initPrefectureData, payload: plateList); 34 | } 35 | 36 | static Action getUserInfo() { 37 | return const Action(IndexAction.getUserInfo); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/routes/index/effect.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flustars/flustars.dart'; 3 | import 'package:flutter_app/global_store/action.dart'; 4 | import 'package:flutter_app/global_store/store.dart'; 5 | import 'package:flutter_app/models/home/plate_entity.dart'; 6 | import 'package:flutter_app/models/user_info_entity.dart'; 7 | import 'package:flutter_app/routes/index/action.dart'; 8 | import 'package:flutter_app/utils/common.dart'; 9 | import 'package:flutter_app/utils/data_tool.dart'; 10 | import 'package:flutter_app/utils/net/dio_utils.dart'; 11 | import 'package:flutter_app/utils/net/http_api.dart'; 12 | import 'package:flutter_app/utils/toast.dart'; 13 | import 'package:flutter_app/widgets/keep_alive_widget.dart'; 14 | import 'state.dart'; 15 | 16 | Effect buildEffect() { 17 | return combineEffects(>{ 18 | Lifecycle.initState: _init, 19 | IndexAction.getUserInfo: _getUserInfo 20 | }); 21 | } 22 | 23 | void _init(Action action, Context ctx) async { 24 | dynamic pageList = [ 25 | keepAliveWrapper( 26 | ctx.buildComponent('homePage'), 27 | ), 28 | keepAliveWrapper( 29 | ctx.buildComponent('classPage'), 30 | ), 31 | keepAliveWrapper( 32 | ctx.buildComponent('prefecturePage'), 33 | ), 34 | keepAliveWrapper( 35 | ctx.buildComponent('minePage'), 36 | ), 37 | ]; 38 | ctx.dispatch(IndexActionCreator.initPageList(pageList)); 39 | 40 | //读取首页缓存数据 41 | await SpUtil.getInstance(); 42 | judgeLogin(successCallback: () { 43 | println('准备获取用户数据'); 44 | ctx.dispatch(IndexActionCreator.getUserInfo()); 45 | }); 46 | List homeCacheData = SpUtil.getObjectList(Constant.homeData); 47 | 48 | List homeData = []; 49 | for (dynamic item in homeCacheData) { 50 | homeData.add(PlateEntity.fromJson(item)); 51 | } 52 | ctx.dispatch(IndexActionCreator.initHomeData(homeData)); 53 | 54 | //读取首页云学术缓存数据 55 | List classCacheData = SpUtil.getObjectList(Constant.classData); 56 | 57 | List classData = []; 58 | for (dynamic item in classCacheData) { 59 | classData.add(PlateEntity.fromJson(item)); 60 | } 61 | ctx.dispatch(IndexActionCreator.initClassData(classData)); 62 | 63 | //读取专区缓存数据 64 | List prefectureCacheData = 65 | SpUtil.getObjectList(Constant.prefectureData); 66 | 67 | List prefectureData = []; 68 | for (dynamic item in prefectureCacheData) { 69 | prefectureData.add(PlateEntity.fromJson(item)); 70 | } 71 | ctx.dispatch(IndexActionCreator.initPrefectureData(prefectureData)); 72 | } 73 | 74 | void _getUserInfo(Action action, Context ctx) async { 75 | DioUtils.instance.requestNetwork( 76 | Method.get, HttpApi.getUserInfo, params: null, queryParameters: null, 77 | onSuccess: (data) async { 78 | println('获取用户数据' + data.accountInfo.name); 79 | GlobalStore.store.dispatch(GlobalActionCreator.updateUserInfo(data)); 80 | }, onError: (code, msg) { 81 | Toast.show(msg); 82 | }); 83 | } 84 | -------------------------------------------------------------------------------- /lib/routes/index/page.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter_app/routes/class_module/class_page/page.dart'; 3 | import 'package:flutter_app/routes/home_module/home_page/page.dart'; 4 | import 'package:flutter_app/routes/mine_module/mine_page/page.dart'; 5 | import 'package:flutter_app/routes/prefecture_module/prefecture_page/page.dart'; 6 | import 'effect.dart'; 7 | import 'reducer.dart'; 8 | import 'state.dart'; 9 | import 'view.dart'; 10 | 11 | class IndexPage extends Page> { 12 | IndexPage() 13 | : super( 14 | initState: initState, 15 | effect: buildEffect(), 16 | reducer: buildReducer(), 17 | view: buildView, 18 | dependencies: Dependencies( 19 | adapter: null, 20 | slots: >{ 21 | 'homePage': HomeConnector() + HomePage(), 22 | 'classPage': ClassConnector() + ClassPage(), 23 | 'prefecturePage': PrefectureConnector() + PrefecturePage(), 24 | 'minePage': MineConnector() + MinePage() 25 | }), 26 | middleware: >[], 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /lib/routes/index/reducer.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | import 'action.dart'; 4 | import 'state.dart'; 5 | 6 | Reducer buildReducer() { 7 | return asReducer( 8 | >{ 9 | IndexAction.jump: _jump, 10 | IndexAction.initPageList: _init, 11 | IndexAction.initHomeData:_initHomeData, 12 | IndexAction.initClassData:_initClassData, 13 | IndexAction.initPrefectureData:_initPrefectureData 14 | }, 15 | ); 16 | } 17 | 18 | IndexState _jump(IndexState state, Action action) { 19 | final int newIndex = action.payload; 20 | return state.clone() 21 | ..currentIndex = newIndex; 22 | } 23 | 24 | IndexState _init(IndexState state, Action action) { 25 | final dynamic pageList = action.payload; 26 | return state.clone() 27 | ..pageList = pageList; 28 | } 29 | 30 | IndexState _initHomeData(IndexState state, Action action) { 31 | return state.clone()..homePlateList = action.payload; 32 | } 33 | 34 | IndexState _initClassData(IndexState state, Action action) { 35 | return state.clone()..classPlateList = action.payload; 36 | } 37 | 38 | IndexState _initPrefectureData(IndexState state, Action action) { 39 | return state.clone()..prefecturePlateList = action.payload; 40 | } 41 | 42 | -------------------------------------------------------------------------------- /lib/routes/index/state.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_app/global_store/state.dart'; 4 | import 'package:flutter_app/models/home/plate_entity.dart'; 5 | import 'package:flutter_app/models/user_info_entity.dart'; 6 | import 'package:flutter_app/routes/class_module/class_page/state.dart'; 7 | import 'package:flutter_app/routes/home_module/home_page/state.dart'; 8 | import 'package:flutter_app/routes/mine_module/mine_page/state.dart'; 9 | import 'package:flutter_app/routes/prefecture_module/prefecture_page/state.dart'; 10 | 11 | class IndexState implements GlobalBaseState, Cloneable { 12 | int currentIndex; 13 | PageController pageController; 14 | dynamic pageList; 15 | List homePlateList; 16 | List classPlateList; 17 | List prefecturePlateList; 18 | 19 | @override 20 | IndexState clone() { 21 | return IndexState() 22 | ..currentIndex = currentIndex 23 | ..pageController = PageController() 24 | ..pageList = pageList 25 | ..homePlateList = homePlateList 26 | ..classPlateList = classPlateList 27 | ..prefecturePlateList = prefecturePlateList 28 | ..userInfo = userInfo; 29 | } 30 | 31 | @override 32 | UserInfoEntity userInfo; 33 | } 34 | 35 | IndexState initState(Map args) { 36 | return IndexState().clone() 37 | ..currentIndex = 0 38 | ..pageController = PageController() 39 | ..pageList = [] 40 | ..homePlateList = [] 41 | ..classPlateList = [] 42 | ..prefecturePlateList = []; 43 | } 44 | 45 | //首页 46 | class HomeConnector extends ConnOp 47 | with ReselectMixin { 48 | @override 49 | HomeState computed(IndexState state) { 50 | return HomeState()..plateList = state.homePlateList 51 | ..userInfo = state.userInfo; 52 | } 53 | 54 | @override 55 | List factors(IndexState state) { 56 | return [state.homePlateList.length,state.userInfo]; 57 | } 58 | 59 | @override 60 | void set(IndexState state, HomeState subState) { 61 | // throw Exception('Unexcepted to set PageState from HomeState'); 62 | } 63 | } 64 | 65 | //云学术 66 | class ClassConnector extends ConnOp 67 | with ReselectMixin { 68 | @override 69 | ClassState computed(IndexState state) { 70 | return ClassState()..plateList = state.classPlateList; 71 | } 72 | 73 | @override 74 | List factors(IndexState state) { 75 | return [state.classPlateList.length]; 76 | } 77 | 78 | @override 79 | void set(IndexState state, ClassState subState) { 80 | throw Exception('Unexcepted to set PageState from ClassState'); 81 | } 82 | } 83 | 84 | //工作台 85 | class PrefectureConnector extends ConnOp 86 | with ReselectMixin { 87 | @override 88 | PrefectureState computed(IndexState state) { 89 | return PrefectureState()..plateList = state.prefecturePlateList; 90 | } 91 | 92 | @override 93 | List factors(IndexState state) { 94 | return [state.prefecturePlateList.length]; 95 | } 96 | 97 | @override 98 | void set(IndexState state, PrefectureState subState) { 99 | throw Exception('Unexcepted to set PageState from PrefectureState'); 100 | } 101 | } 102 | 103 | //我的 104 | class MineConnector extends ConnOp 105 | with ReselectMixin { 106 | @override 107 | MineState computed(IndexState state) { 108 | return MineState()..userInfo = state.userInfo; 109 | } 110 | 111 | @override 112 | List factors(IndexState state) { 113 | return [state.userInfo]; 114 | } 115 | 116 | @override 117 | void set(IndexState state, MineState subState) { 118 | throw Exception('Unexcepted to set PageState from MineState'); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /lib/routes/index/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_app/res/colors.dart'; 4 | import 'package:flutter_app/res/dimens.dart'; 5 | import 'package:flutter_app/routes/index/action.dart'; 6 | import 'package:flutter_app/utils/double_tap_back_exit_app.dart'; 7 | import 'package:flutter_app/utils/screen_util.dart'; 8 | import 'package:flutter_app/utils/theme_utils.dart'; 9 | import 'package:flutter_app/widgets/load_image.dart'; 10 | import 'state.dart'; 11 | 12 | Widget buildView(IndexState state, Dispatch dispatch, ViewService viewService) { 13 | initScreenUtil(viewService.context); 14 | return DoubleTapBackExitApp( 15 | child: Scaffold( 16 | bottomNavigationBar: BottomNavigationBar( 17 | backgroundColor: ThemeUtils.getBackgroundColor(viewService.context), 18 | items: _buildBottomNavigationBarItem(), 19 | type: BottomNavigationBarType.fixed, 20 | currentIndex: state.currentIndex, 21 | elevation: 5.0, 22 | iconSize: 21.0, 23 | selectedFontSize: Dimens.font_sp10, 24 | unselectedFontSize: Dimens.font_sp10, 25 | selectedItemColor: MyColors.app_main, 26 | unselectedItemColor: MyColors.unselected_item_color, 27 | onTap: (index) { 28 | state.pageController.jumpToPage(index); 29 | dispatch(IndexActionCreator.jump(index)); 30 | }, 31 | ), 32 | // 使用PageView的原因参看 https://zhuanlan.zhihu.com/p/58582876 33 | body: PageView( 34 | controller: state.pageController, 35 | onPageChanged: (int index) { 36 | _onPageChanged(index); 37 | }, 38 | children: state.pageList, 39 | physics: NeverScrollableScrollPhysics(), 40 | ))); 41 | } 42 | 43 | void _onPageChanged(int index) {} 44 | 45 | List _buildBottomNavigationBarItem() { 46 | var _appBarTitles = ['首页', '云学术', '专区', '我的']; 47 | List _list; 48 | if (_list == null) { 49 | var _tabImages = [ 50 | [ 51 | const LoadAssetImage("ico_tabar_home_normal", width: 25.0), 52 | const LoadAssetImage("ico_tabar_home_pressed", width: 25.0), 53 | ], 54 | [ 55 | const LoadAssetImage("ico_tabar_class_normal", width: 25.0), 56 | const LoadAssetImage("ico_tabar_class_pressed", width: 25.0), 57 | ], 58 | [ 59 | const LoadAssetImage("ico_tabar_prefecture_normal", width: 25.0), 60 | const LoadAssetImage("ico_tabar_prefecture_pressed", width: 25.0), 61 | ], 62 | [ 63 | const LoadAssetImage("ico_tabar_mine_normal", width: 25.0), 64 | const LoadAssetImage("ico_tabar_mine_pressed", width: 25.0), 65 | ] 66 | ]; 67 | _list = List.generate(4, (i) { 68 | return BottomNavigationBarItem( 69 | icon: _tabImages[i][0], 70 | activeIcon: _tabImages[i][1], 71 | title: Padding( 72 | padding: const EdgeInsets.only(top: 1.5), 73 | child: Text( 74 | _appBarTitles[i], 75 | key: Key(_appBarTitles[i]), 76 | ), 77 | )); 78 | }); 79 | } 80 | return _list; 81 | } 82 | -------------------------------------------------------------------------------- /lib/routes/login_page/action.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:webview_flutter/webview_flutter.dart'; 3 | 4 | //TODO replace with your own action 5 | enum LoginAction { 6 | changeClick, 7 | initWebView, 8 | onPwdLogin, 9 | updateMobile, 10 | updatePwd, 11 | getUserInfo 12 | } 13 | 14 | class LoginActionCreator { 15 | static Action changeClick(bool clickState) { 16 | return Action(LoginAction.changeClick, payload: clickState); 17 | } 18 | 19 | static Action initWebView(WebViewController webViewController) { 20 | return Action(LoginAction.initWebView, payload: webViewController); 21 | } 22 | 23 | static Action onPwdLogin(String message) { 24 | return Action(LoginAction.onPwdLogin, payload: message); 25 | } 26 | 27 | static Action updateMobile(String mobile) { 28 | return Action(LoginAction.updateMobile, payload: mobile); 29 | } 30 | 31 | static Action updatePwd(String pwd) { 32 | return Action(LoginAction.updatePwd, payload: pwd); 33 | } 34 | 35 | static Action getUserInfo() { 36 | return const Action(LoginAction.getUserInfo); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/routes/login_page/auth_button/action.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | //TODO replace with your own action 4 | enum AuthButtonAction { action } 5 | 6 | class AuthButtonActionCreator { 7 | static Action onAction() { 8 | return const Action(AuthButtonAction.action); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/routes/login_page/auth_button/component.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | import 'effect.dart'; 4 | import 'reducer.dart'; 5 | import 'state.dart'; 6 | import 'view.dart'; 7 | 8 | class AuthButtonComponent extends Component { 9 | AuthButtonComponent() 10 | : super( 11 | effect: buildEffect(), 12 | reducer: buildReducer(), 13 | view: buildView, 14 | dependencies: Dependencies( 15 | adapter: null, 16 | slots: >{ 17 | }),); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /lib/routes/login_page/auth_button/effect.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'action.dart'; 3 | import 'state.dart'; 4 | 5 | Effect buildEffect() { 6 | return combineEffects(>{ 7 | AuthButtonAction.action: _onAction, 8 | }); 9 | } 10 | 11 | void _onAction(Action action, Context ctx) { 12 | } 13 | -------------------------------------------------------------------------------- /lib/routes/login_page/auth_button/reducer.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | import 'action.dart'; 4 | import 'state.dart'; 5 | 6 | Reducer buildReducer() { 7 | return asReducer( 8 | >{ 9 | AuthButtonAction.action: _onAction, 10 | }, 11 | ); 12 | } 13 | 14 | AuthButtonState _onAction(AuthButtonState state, Action action) { 15 | final AuthButtonState newState = state.clone(); 16 | return newState; 17 | } 18 | -------------------------------------------------------------------------------- /lib/routes/login_page/auth_button/state.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:webview_flutter/webview_flutter.dart'; 3 | 4 | class AuthButtonState implements Cloneable { 5 | WebViewController webViewController; 6 | bool canClick; 7 | 8 | AuthButtonState({this.canClick, this.webViewController}); 9 | 10 | @override 11 | AuthButtonState clone() { 12 | return AuthButtonState() 13 | ..canClick = canClick 14 | ..webViewController = webViewController; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/routes/login_page/auth_button/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_app/routes/login_page/action.dart'; 4 | import 'package:flutter_app/utils/screen_util.dart'; 5 | import 'package:webview_flutter/webview_flutter.dart'; 6 | import 'state.dart'; 7 | 8 | Widget buildView( 9 | AuthButtonState state, Dispatch dispatch, ViewService viewService) { 10 | if (state.webViewController != null && state.canClick) { 11 | state.webViewController.evaluateJavascript( 12 | "window.document.getElementsByTagName('html')[0].style.background = '#34b0b0';" 13 | "window.document.getElementById('SM_BTN_WRAPPER_1').style.pointerEvents = 'auto';"); 14 | } else if (state.webViewController != null) { 15 | state.webViewController.evaluateJavascript( 16 | "window.document.getElementsByTagName('html')[0].style.background = '#d9d9d9';" 17 | "window.document.getElementById('SM_BTN_WRAPPER_1').style.pointerEvents = 'none';"); 18 | } 19 | return Container( 20 | width: ScreenUtil.screenWidthDp, 21 | margin: EdgeInsets.only(top: scaleSize(54)), 22 | child: Row( 23 | mainAxisAlignment: MainAxisAlignment.center, 24 | crossAxisAlignment: CrossAxisAlignment.center, 25 | children: [ 26 | Container( 27 | width: scaleSize(315), 28 | height: scaleSize(50), 29 | child: WebView( 30 | initialUrl: 'http://codepush.guigug.com/flutter.html', 31 | debuggingEnabled: true, 32 | javascriptMode: JavascriptMode.unrestricted, 33 | onWebViewCreated: (WebViewController controller) { 34 | dispatch(LoginActionCreator.initWebView(controller)); 35 | }, 36 | onPageFinished: (url) {}, 37 | javascriptChannels: [ 38 | JavascriptChannel( 39 | name: "feedback", 40 | onMessageReceived: (JavascriptMessage message) { 41 | dispatch( 42 | LoginActionCreator.onPwdLogin(message.message)); 43 | print("参数: ${message.message}"); 44 | }), 45 | ].toSet(), 46 | ), 47 | ) 48 | ], 49 | ), 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /lib/routes/login_page/effect.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:fish_redux/fish_redux.dart'; 4 | import 'package:flustars/flustars.dart'; 5 | import 'package:flutter_app/global_store/action.dart'; 6 | import 'package:flutter_app/global_store/store.dart'; 7 | import 'package:flutter_app/models/user_info_entity.dart'; 8 | import 'package:flutter_app/routes/login_page/action.dart'; 9 | import 'package:flutter_app/utils/common.dart'; 10 | import 'package:flutter_app/utils/native_method.dart'; 11 | import 'package:flutter_app/utils/net/dio_utils.dart'; 12 | import 'package:flutter_app/utils/net/http_api.dart'; 13 | import 'package:flutter_app/utils/progress.dart'; 14 | import 'package:flutter_app/utils/routes/fluro_navigator.dart'; 15 | import 'package:flutter_app/utils/toast.dart'; 16 | import 'state.dart'; 17 | 18 | Effect buildEffect() { 19 | return combineEffects(>{ 20 | LoginAction.onPwdLogin: _onPwdLogin, 21 | LoginAction.getUserInfo: _getUserInfo, 22 | }); 23 | } 24 | 25 | /// *@author 何晏波 26 | /// *@QQ 1054539528 27 | /// *@date 2020-01-15 28 | /// *@Description: 获取到智能按钮数据后调用登录接口 29 | void _onPwdLogin(Action action, Context ctx) async { 30 | String cipherPhone = await encode(wrapWithTimestamps(ctx.state.mobile)); 31 | String cipherPwd = await encode(wrapWithTimestamps(ctx.state.pwd)); 32 | Map message = json.decode(action.payload); 33 | showProgress(ctx.context); 34 | Map params = Map(); 35 | params["mobile"] = cipherPhone; 36 | params["password"] = cipherPwd; 37 | params["vscene"] = message['vscene']; 38 | params["vtoken"] = message['vtoken']; 39 | params["vsessionId"] = message['vsessionId']; 40 | params["vsig"] = message['vsig']; 41 | DioUtils.instance.requestNetwork(Method.post, HttpApi.password, 42 | params: params, queryParameters: params, onSuccess: (data) async { 43 | hideProgress(ctx.context); 44 | await SpUtil.putString(Constant.loginToken, data); 45 | println('执行到这里啦'); 46 | ctx.dispatch(LoginActionCreator.getUserInfo()); 47 | }, onError: (code, msg) { 48 | hideProgress(ctx.context); 49 | Toast.show(msg); 50 | }); 51 | } 52 | 53 | /// *@author 何晏波 54 | /// *@QQ 1054539528 55 | /// *@date 2020-01-17 56 | /// *@Description: 获取用户信息 57 | void _getUserInfo(Action action, Context ctx) async { 58 | DioUtils.instance.requestNetwork(Method.get, HttpApi.getUserInfo, 59 | params: null, queryParameters: null, onSuccess: (data) async { 60 | GlobalStore.store.dispatch(GlobalActionCreator.updateUserInfo(data)); 61 | NavigatorUtils.goBack(ctx.context); 62 | }, onError: (code, msg) { 63 | Toast.show(msg); 64 | }); 65 | } 66 | -------------------------------------------------------------------------------- /lib/routes/login_page/page.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter_app/routes/login_page/auth_button/component.dart'; 3 | 4 | import 'effect.dart'; 5 | import 'reducer.dart'; 6 | import 'state.dart'; 7 | import 'view.dart'; 8 | 9 | class LoginPage extends Page> { 10 | LoginPage() 11 | : super( 12 | initState: initState, 13 | effect: buildEffect(), 14 | reducer: buildReducer(), 15 | view: buildView, 16 | dependencies: Dependencies( 17 | adapter: null, 18 | slots: >{ 19 | 'authButton': AuthButtonConnector() + AuthButtonComponent() 20 | }), 21 | middleware: >[], 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /lib/routes/login_page/reducer.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | import 'action.dart'; 4 | import 'state.dart'; 5 | 6 | Reducer buildReducer() { 7 | return asReducer( 8 | >{ 9 | LoginAction.changeClick: _changeClick, 10 | LoginAction.initWebView: _initWebView, 11 | LoginAction.updateMobile: _updateMobile, 12 | LoginAction.updatePwd: _updatePwd, 13 | }, 14 | ); 15 | } 16 | 17 | LoginState _changeClick(LoginState state, Action action) { 18 | return state.clone()..canClick = action.payload; 19 | } 20 | 21 | LoginState _initWebView(LoginState state, Action action) { 22 | return state.clone()..webViewController = action.payload; 23 | } 24 | 25 | LoginState _updateMobile(LoginState state, Action action) { 26 | return state.clone() 27 | ..mobile = action.payload 28 | ..canClick = action.payload.startsWith('1')&& 29 | action.payload.length == 11&& 30 | state.pwd.length > 5; 31 | } 32 | 33 | LoginState _updatePwd(LoginState state, Action action) { 34 | return state.clone() 35 | ..pwd = action.payload 36 | ..canClick = state.mobile.startsWith('1')&& 37 | state.mobile.length == 11&& 38 | action.payload.length > 5; 39 | } 40 | -------------------------------------------------------------------------------- /lib/routes/login_page/state.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:webview_flutter/webview_flutter.dart'; 3 | 4 | import 'auth_button/state.dart'; 5 | 6 | class LoginState implements Cloneable { 7 | bool canClick; 8 | WebViewController webViewController; 9 | String mobile; 10 | String pwd; 11 | 12 | @override 13 | LoginState clone() { 14 | return LoginState() 15 | ..canClick = canClick 16 | ..webViewController = webViewController 17 | ..mobile = mobile 18 | ..pwd = pwd; 19 | } 20 | } 21 | 22 | LoginState initState(Map args) { 23 | return LoginState() 24 | ..canClick = false 25 | ..mobile = '' 26 | ..pwd = ''; 27 | } 28 | 29 | class AuthButtonConnector extends ConnOp 30 | with ReselectMixin { 31 | @override 32 | AuthButtonState computed(LoginState state) { 33 | return AuthButtonState().clone() 34 | ..canClick = state.canClick 35 | ..webViewController = state.webViewController; 36 | } 37 | 38 | @override 39 | List factors(LoginState state) { 40 | return [state.canClick, state.webViewController]; 41 | } 42 | 43 | @override 44 | void set(LoginState state, AuthButtonState subState) {} 45 | } 46 | -------------------------------------------------------------------------------- /lib/routes/mine_module/collect_page/action.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter_app/models/mine/collect_entity.dart'; 3 | 4 | //TODO replace with your own action 5 | enum CollectAction { 6 | initData, 7 | updatePageNo, 8 | onRefresh, 9 | refresh, 10 | onLoadMore, 11 | loadMore 12 | } 13 | 14 | class CollectActionCreator { 15 | static Action onInitData(CollectEntity collectEntity) { 16 | return Action(CollectAction.initData, payload: collectEntity); 17 | } 18 | 19 | static Action updatePageNo(int newPageNo) { 20 | return Action(CollectAction.updatePageNo, payload: newPageNo); 21 | } 22 | 23 | static Action onRefresh() { 24 | return const Action(CollectAction.onRefresh); 25 | } 26 | 27 | static Action refresh(CollectEntity collectEntity) { 28 | return Action(CollectAction.refresh, payload: collectEntity); 29 | } 30 | 31 | static Action onLoadMore() { 32 | return const Action(CollectAction.onLoadMore); 33 | } 34 | 35 | static Action loadMore(CollectEntity collectEntity) { 36 | return Action(CollectAction.loadMore, payload: collectEntity); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/routes/mine_module/collect_page/effect.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter_app/models/mine/collect_entity.dart'; 3 | import 'package:flutter_app/utils/net/dio_utils.dart'; 4 | import 'package:flutter_app/utils/net/http_api.dart'; 5 | import 'package:flutter_app/utils/progress.dart'; 6 | import 'package:flutter_app/utils/toast.dart'; 7 | import 'action.dart'; 8 | import 'state.dart'; 9 | 10 | Effect buildEffect() { 11 | return combineEffects(>{ 12 | Lifecycle.initState: _initData, 13 | CollectAction.onRefresh: _onRefresh, 14 | CollectAction.onLoadMore: _onLoadMore, 15 | }); 16 | } 17 | 18 | void _initData(Action action, Context ctx) { 19 | Map params = Map(); 20 | params["pageNo"] = '1'; 21 | params["pageSize"] = ctx.state.pageSize.toString(); 22 | Future.delayed(Duration(microseconds: 500), () { 23 | showProgress(ctx.context); 24 | DioUtils.instance.requestNetwork( 25 | Method.get, HttpApi.getCollectRecord, 26 | params: params, queryParameters: params, onSuccess: (data) async { 27 | hideProgress(ctx.context); 28 | ctx.state.controller.resetLoadState(); 29 | ctx.state.controller.finishRefresh(); 30 | ctx.dispatch(CollectActionCreator.updatePageNo(1)); 31 | ctx.dispatch(CollectActionCreator.refresh(data)); 32 | }, onError: (code, msg) { 33 | hideProgress(ctx.context); 34 | Toast.show(msg); 35 | }); 36 | }); 37 | } 38 | 39 | void _onRefresh(Action action, Context ctx) { 40 | Map params = Map(); 41 | params["pageNo"] = '1'; 42 | params["pageSize"] = ctx.state.pageSize.toString(); 43 | Future.delayed(Duration(seconds: 1), () { 44 | DioUtils.instance.requestNetwork( 45 | Method.get, HttpApi.getCollectRecord, 46 | params: params, queryParameters: params, onSuccess: (data) async { 47 | ctx.state.controller.resetLoadState(); 48 | ctx.state.controller.finishRefresh(); 49 | ctx.dispatch(CollectActionCreator.updatePageNo(1)); 50 | ctx.dispatch(CollectActionCreator.refresh(data)); 51 | }, onError: (code, msg) { 52 | Toast.show(msg); 53 | }); 54 | }); 55 | } 56 | 57 | void _onLoadMore(Action action, Context ctx) { 58 | Map params = Map(); 59 | params["pageNo"] = ctx.state.pageNo.toString(); 60 | params["pageSize"] = ctx.state.pageSize.toString(); 61 | CollectEntity collectEntity = ctx.state.collectEntity; 62 | Future.delayed(Duration(seconds: 1), () { 63 | DioUtils.instance.requestNetwork( 64 | Method.get, HttpApi.getCollectRecord, 65 | params: params, queryParameters: params, onSuccess: (data) { 66 | ctx.state.controller.finishLoad(); 67 | ctx.dispatch(CollectActionCreator.updatePageNo(ctx.state.pageNo + 1)); 68 | if (data.result.length != 0) { 69 | collectEntity.result.addAll(data.result); 70 | ctx.dispatch(CollectActionCreator.loadMore(collectEntity)); 71 | } else { 72 | Toast.show('没有更多数据了'); 73 | } 74 | }, onError: (code, msg) { 75 | Toast.show(msg); 76 | }); 77 | }); 78 | } 79 | -------------------------------------------------------------------------------- /lib/routes/mine_module/collect_page/page.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | import 'effect.dart'; 4 | import 'reducer.dart'; 5 | import 'state.dart'; 6 | import 'view.dart'; 7 | 8 | class CollectPage extends Page> { 9 | CollectPage() 10 | : super( 11 | initState: initState, 12 | effect: buildEffect(), 13 | reducer: buildReducer(), 14 | view: buildView, 15 | dependencies: Dependencies( 16 | adapter: null, 17 | slots: >{ 18 | }), 19 | middleware: >[ 20 | ],); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /lib/routes/mine_module/collect_page/reducer.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | import 'action.dart'; 4 | import 'state.dart'; 5 | 6 | Reducer buildReducer() { 7 | return asReducer( 8 | >{ 9 | CollectAction.initData: _initData, 10 | CollectAction.updatePageNo: _updatePageNo, 11 | CollectAction.refresh: _refresh, 12 | CollectAction.loadMore: _loadMore, 13 | }, 14 | ); 15 | } 16 | 17 | CollectState _initData(CollectState state, Action action) { 18 | return state.clone() 19 | ..collectEntity = action.payload; 20 | } 21 | 22 | CollectState _updatePageNo(CollectState state, Action action) { 23 | return state.clone() 24 | ..pageNo = action.payload; 25 | } 26 | 27 | CollectState _refresh(CollectState state, Action action) { 28 | return state.clone() 29 | ..collectEntity = action.payload; 30 | } 31 | 32 | CollectState _loadMore(CollectState state, Action action) { 33 | return state.clone() 34 | ..collectEntity = action.payload; 35 | } 36 | -------------------------------------------------------------------------------- /lib/routes/mine_module/collect_page/state.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter_app/models/mine/collect_entity.dart'; 3 | import 'package:flutter_easyrefresh/easy_refresh.dart'; 4 | 5 | class CollectState implements Cloneable { 6 | CollectEntity collectEntity; 7 | EasyRefreshController controller; 8 | int pageNo; 9 | int pageSize; 10 | 11 | @override 12 | CollectState clone() { 13 | return CollectState() 14 | ..collectEntity = collectEntity 15 | ..controller = controller 16 | ..pageNo = pageNo 17 | ..pageSize = pageSize; 18 | } 19 | } 20 | 21 | CollectState initState(Map args) { 22 | return CollectState() 23 | ..collectEntity = null 24 | ..controller = EasyRefreshController() 25 | ..pageNo = 1 26 | ..pageSize = 20; 27 | } 28 | -------------------------------------------------------------------------------- /lib/routes/mine_module/collect_page/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_app/models/home/plate_entity.dart'; 4 | import 'package:flutter_app/res/colors.dart'; 5 | import 'package:flutter_app/routes/home_module/home_page/widget/home_page_list_tile.dart'; 6 | import 'package:flutter_app/routes/mine_module/collect_page/action.dart'; 7 | import 'package:flutter_app/widgets/top_header.dart'; 8 | import 'package:flutter_easyrefresh/bezier_bounce_footer.dart'; 9 | import 'package:flutter_easyrefresh/bezier_circle_header.dart'; 10 | import 'package:flutter_easyrefresh/easy_refresh.dart'; 11 | 12 | import 'state.dart'; 13 | 14 | Widget buildView( 15 | CollectState state, Dispatch dispatch, ViewService viewService) { 16 | return Container( 17 | color: Colors.white, 18 | child: Column( 19 | children: [ 20 | TopHeader( 21 | title: '我的收藏', 22 | ), 23 | Expanded( 24 | child: EasyRefresh.custom( 25 | enableControlFinishRefresh: true, 26 | enableControlFinishLoad: true, 27 | taskIndependence: false, 28 | controller: state.controller, 29 | scrollDirection: Axis.vertical, 30 | topBouncing: true, 31 | bottomBouncing: true, 32 | header: BezierCircleHeader(backgroundColor: MyColors.themeColor), 33 | footer: BezierBounceFooter(backgroundColor: MyColors.themeColor), 34 | onRefresh: () async { 35 | dispatch(CollectActionCreator.onRefresh()); 36 | }, 37 | onLoad: () async { 38 | dispatch(CollectActionCreator.onLoadMore()); 39 | }, 40 | slivers: [ 41 | SliverList( 42 | delegate: SliverChildBuilderDelegate( 43 | (context, index) { 44 | PlateItem resultEntity = 45 | state.collectEntity.result[index]; 46 | return HomePageListTile( 47 | item: resultEntity,); 48 | }, 49 | childCount: state.collectEntity != null 50 | ? state.collectEntity.result.length 51 | : 0, 52 | ), 53 | ), 54 | ], 55 | ), 56 | ) 57 | ], 58 | ), 59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /lib/routes/mine_module/mine_page/action.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | //TODO replace with your own action 4 | enum MineAction { action } 5 | 6 | class MineActionCreator { 7 | static Action onAction() { 8 | return const Action(MineAction.action); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/routes/mine_module/mine_page/effect.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'action.dart'; 3 | import 'state.dart'; 4 | 5 | Effect buildEffect() { 6 | return combineEffects(>{ 7 | MineAction.action: _onAction, 8 | }); 9 | } 10 | 11 | void _onAction(Action action, Context ctx) { 12 | } 13 | -------------------------------------------------------------------------------- /lib/routes/mine_module/mine_page/page.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | import 'effect.dart'; 4 | import 'reducer.dart'; 5 | import 'state.dart'; 6 | import 'view.dart'; 7 | 8 | class MinePage extends Page> { 9 | MinePage() 10 | : super( 11 | initState: initState, 12 | effect: buildEffect(), 13 | reducer: buildReducer(), 14 | view: buildView, 15 | dependencies: Dependencies( 16 | adapter: null, 17 | slots: >{ 18 | }), 19 | middleware: >[ 20 | ],); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /lib/routes/mine_module/mine_page/reducer.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | import 'action.dart'; 4 | import 'state.dart'; 5 | 6 | Reducer buildReducer() { 7 | return asReducer( 8 | >{ 9 | MineAction.action: _onAction, 10 | }, 11 | ); 12 | } 13 | 14 | MineState _onAction(MineState state, Action action) { 15 | final MineState newState = state.clone(); 16 | return newState; 17 | } 18 | -------------------------------------------------------------------------------- /lib/routes/mine_module/mine_page/state.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter_app/global_store/state.dart'; 3 | import 'package:flutter_app/models/user_info_entity.dart'; 4 | 5 | class MineState implements GlobalBaseState, Cloneable { 6 | 7 | @override 8 | MineState clone() { 9 | return MineState() 10 | ..userInfo = userInfo; 11 | } 12 | 13 | @override 14 | UserInfoEntity userInfo; 15 | } 16 | 17 | MineState initState(Map args) { 18 | return MineState(); 19 | } 20 | -------------------------------------------------------------------------------- /lib/routes/mine_module/setting_page/action.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | //TODO replace with your own action 4 | enum SettingAction { action } 5 | 6 | class SettingActionCreator { 7 | static Action onAction() { 8 | return const Action(SettingAction.action); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/routes/mine_module/setting_page/effect.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'action.dart'; 3 | import 'state.dart'; 4 | 5 | Effect buildEffect() { 6 | return combineEffects(>{ 7 | SettingAction.action: _onAction, 8 | }); 9 | } 10 | 11 | void _onAction(Action action, Context ctx) { 12 | } 13 | -------------------------------------------------------------------------------- /lib/routes/mine_module/setting_page/page.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | import 'effect.dart'; 4 | import 'reducer.dart'; 5 | import 'state.dart'; 6 | import 'view.dart'; 7 | 8 | class SettingPage extends Page> { 9 | SettingPage() 10 | : super( 11 | initState: initState, 12 | effect: buildEffect(), 13 | reducer: buildReducer(), 14 | view: buildView, 15 | dependencies: Dependencies( 16 | adapter: null, 17 | slots: >{ 18 | }), 19 | middleware: >[ 20 | ],); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /lib/routes/mine_module/setting_page/reducer.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | import 'action.dart'; 4 | import 'state.dart'; 5 | 6 | Reducer buildReducer() { 7 | return asReducer( 8 | >{ 9 | SettingAction.action: _onAction, 10 | }, 11 | ); 12 | } 13 | 14 | SettingState _onAction(SettingState state, Action action) { 15 | final SettingState newState = state.clone(); 16 | return newState; 17 | } 18 | -------------------------------------------------------------------------------- /lib/routes/mine_module/setting_page/state.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter_app/global_store/state.dart'; 3 | import 'package:flutter_app/models/user_info_entity.dart'; 4 | 5 | class SettingState implements GlobalBaseState,Cloneable { 6 | 7 | @override 8 | SettingState clone() { 9 | return SettingState() 10 | ..userInfo = userInfo; 11 | } 12 | 13 | @override 14 | UserInfoEntity userInfo; 15 | } 16 | 17 | SettingState initState(Map args) { 18 | return SettingState(); 19 | } 20 | -------------------------------------------------------------------------------- /lib/routes/mine_module/setting_page/view.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_app/res/colors.dart'; 4 | import 'package:flutter_app/res/gaps.dart'; 5 | import 'package:flutter_app/utils/screen_util.dart'; 6 | import 'package:flutter_app/widgets/list_subtitle.dart'; 7 | import 'package:flutter_app/widgets/top_header.dart'; 8 | 9 | import '../exit_dialog.dart'; 10 | import 'state.dart'; 11 | 12 | Widget buildView( 13 | SettingState state, Dispatch dispatch, ViewService viewService) { 14 | return Container( 15 | color: Colors.white, 16 | child: Column( 17 | children: [ 18 | TopHeader( 19 | title: '更多设置', 20 | ), 21 | Expanded( 22 | child: Container( 23 | color: MyColors.pageDefaultBackgroundColor, 24 | child: Column( 25 | children: [ 26 | SizedBox( 27 | height: scaleSize(5), 28 | ), 29 | ListSubtitle( 30 | title: '关于医点数据', 31 | ), 32 | ListSubtitle( 33 | title: '用户协议', 34 | ), 35 | ListSubtitle( 36 | title: '隐私协议', 37 | ), 38 | state.userInfo != null 39 | ? ListSubtitle( 40 | title: '当前帐号', 41 | subTitle: state.userInfo.accountInfo.mobile, 42 | ) 43 | : Gaps.empty, 44 | Container( 45 | margin: EdgeInsets.only(top: scaleSize(10)), 46 | width: ScreenUtil.screenWidthDp, 47 | height: scaleSize(58), 48 | color: Colors.white, 49 | child: FlatButton( 50 | padding: EdgeInsets.all(0), 51 | onPressed: () { 52 | showExitDialog(viewService.context); 53 | }, 54 | child: Center( 55 | child: Text( 56 | '退出登录', 57 | style: TextStyle( 58 | color: Color(0xff182222), 59 | fontSize: setSp(16), 60 | fontWeight: FontWeight.normal, 61 | decoration: TextDecoration.none), 62 | ), 63 | ), 64 | ), 65 | ) 66 | ], 67 | ), 68 | ), 69 | ) 70 | ], 71 | ), 72 | ); 73 | } 74 | -------------------------------------------------------------------------------- /lib/routes/prefecture_module/prefecture_page/action.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | //TODO replace with your own action 4 | enum PrefectureAction { onRefresh } 5 | 6 | class PrefectureActionCreator { 7 | static Action onRefresh() { 8 | return const Action(PrefectureAction.onRefresh); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/routes/prefecture_module/prefecture_page/effect.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flustars/flustars.dart'; 3 | import 'package:flutter_app/models/home/plate_entity.dart'; 4 | import 'package:flutter_app/routes/index/action.dart'; 5 | import 'package:flutter_app/utils/common.dart'; 6 | import 'package:flutter_app/utils/net/dio_utils.dart'; 7 | import 'package:flutter_app/utils/net/http_api.dart'; 8 | import 'package:flutter_app/utils/toast.dart'; 9 | import 'action.dart'; 10 | import 'state.dart'; 11 | 12 | Effect buildEffect() { 13 | return combineEffects(>{ 14 | Lifecycle.initState: _init, 15 | PrefectureAction.onRefresh: _init 16 | }); 17 | } 18 | 19 | Future _init(Action action, Context ctx) async { 20 | await DioUtils.instance.requestNetwork( 21 | Method.get, HttpApi.special, 22 | params: null, 23 | queryParameters: null, 24 | isList: true, onSuccessList: (list) async { 25 | await SpUtil.putObjectList(Constant.prefectureData, list); 26 | ctx.dispatch(IndexActionCreator.initPrefectureData(list)); 27 | }, onError: (code, msg) { 28 | Toast.show(msg); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /lib/routes/prefecture_module/prefecture_page/page.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | import 'effect.dart'; 4 | import 'reducer.dart'; 5 | import 'state.dart'; 6 | import 'view.dart'; 7 | 8 | class PrefecturePage extends Page> { 9 | PrefecturePage() 10 | : super( 11 | initState: initState, 12 | effect: buildEffect(), 13 | reducer: buildReducer(), 14 | view: buildView, 15 | dependencies: Dependencies( 16 | adapter: null, 17 | slots: >{ 18 | }), 19 | middleware: >[ 20 | ],); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /lib/routes/prefecture_module/prefecture_page/reducer.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | 3 | import 'state.dart'; 4 | 5 | Reducer buildReducer() { 6 | return asReducer( 7 | >{ 8 | }, 9 | ); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /lib/routes/prefecture_module/prefecture_page/state.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:flutter_app/models/home/plate_entity.dart'; 3 | 4 | class PrefectureState implements Cloneable { 5 | List plateList; 6 | @override 7 | PrefectureState clone() { 8 | return PrefectureState() 9 | ..plateList = plateList; 10 | } 11 | } 12 | 13 | PrefectureState initState(Map args) { 14 | return PrefectureState() 15 | ..plateList = []; 16 | } 17 | -------------------------------------------------------------------------------- /lib/utils/common.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class Constant { 4 | /// debug开关,上线需要关闭 5 | /// App运行在Release环境时,inProduction为true;当App运行在Debug和Profile环境时,inProduction为false 6 | static const bool inProduction = const bool.fromEnvironment("dart.vm.product"); 7 | 8 | static bool isTest = false; 9 | 10 | static const String data = 'data'; 11 | static const String message = 'message'; 12 | static const String code = 'code'; 13 | static const String loginToken = 'loginToken'; 14 | static const String homeData = "homeData"; 15 | static const String classData = "classData"; 16 | static const String prefectureData = "prefectureData"; 17 | 18 | static const String normal = "normal"; 19 | static const String doctor = "doctor"; 20 | static const String sales = "sales"; 21 | static const String clerk = "clerk"; 22 | static const String nurse = "nurse"; 23 | static const String physician = "physician"; 24 | 25 | static const List colorList = [ 26 | Color(0xFFFFD147), Color(0xFFA9DAF2), Color(0xFFFAAF64), 27 | Color(0xFF7087FA), Color(0xFFA0E65C), Color(0xFF5CE6A1), Color(0xFFA364FA), 28 | Color(0xFFDA61F2), Color(0xFFFA64AE), Color(0xFFFA6464), 29 | ]; 30 | } 31 | -------------------------------------------------------------------------------- /lib/utils/data_tool.dart: -------------------------------------------------------------------------------- 1 | import 'package:flustars/flustars.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | 4 | import 'common.dart'; 5 | 6 | void judgeLogin({@required Function successCallback, Function failCallback}) { 7 | String loginToken = SpUtil.getString(Constant.loginToken); 8 | 9 | if (loginToken != null && loginToken.length != 0) { 10 | if (successCallback != null) { 11 | successCallback(); 12 | } 13 | } else if (failCallback != null) { 14 | failCallback(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/utils/double_tap_back_exit_app.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_app/utils/toast.dart'; 4 | 5 | class DoubleTapBackExitApp extends StatefulWidget { 6 | 7 | const DoubleTapBackExitApp({ 8 | Key key, 9 | @required this.child, 10 | this.duration: const Duration(milliseconds: 2500), 11 | }): super(key: key); 12 | 13 | final Widget child; 14 | /// 两次点击返回按钮的时间间隔 15 | final Duration duration; 16 | 17 | @override 18 | _DoubleTapBackExitAppState createState() => _DoubleTapBackExitAppState(); 19 | } 20 | 21 | class _DoubleTapBackExitAppState extends State { 22 | 23 | DateTime _lastTime; 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return WillPopScope( 28 | onWillPop: _isExit, 29 | child: widget.child, 30 | ); 31 | } 32 | 33 | Future _isExit() { 34 | if (_lastTime == null || DateTime.now().difference(_lastTime) > widget.duration) { 35 | _lastTime = DateTime.now(); 36 | Toast.show("再次点击退出应用"); 37 | return Future.value(false); 38 | } 39 | Toast.cancelToast(); 40 | return Future.value(true); 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /lib/utils/image_utils.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:common_utils/common_utils.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | import 'package:cached_network_image/cached_network_image.dart'; 6 | 7 | class ImageUtils { 8 | 9 | static ImageProvider getAssetImage(String name, {String format: 'png'}) { 10 | return AssetImage(getImgPath(name, format: format)); 11 | } 12 | 13 | static String getImgPath(String name, {String format: 'png'}) { 14 | return 'assets/images/$name.$format'; 15 | } 16 | 17 | static ImageProvider getImageProvider(String imageUrl, {String holderImg: "none"}) { 18 | if (TextUtil.isEmpty(imageUrl)) { 19 | return AssetImage(getImgPath(holderImg)); 20 | } 21 | return CachedNetworkImageProvider(imageUrl); 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /lib/utils/log_utils.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:convert' as convert; 3 | import 'package:common_utils/common_utils.dart'; 4 | import 'common.dart'; 5 | 6 | /// *@filename log_utils.dart 7 | /// *@author 何晏波 8 | /// *@QQ 1054539528 9 | /// *@date 2020-01-14 10 | /// *@Description: 输出Log工具类 11 | class Log{ 12 | 13 | static const String tag = 'Flutter-LOG'; 14 | 15 | static init() { 16 | LogUtil.debuggable = !Constant.inProduction; 17 | } 18 | 19 | static d(String msg, {tag: tag}) { 20 | if (!Constant.inProduction) { 21 | LogUtil.v(msg, tag: tag); 22 | } 23 | } 24 | 25 | static e(String msg, {tag: tag}) { 26 | if (!Constant.inProduction) { 27 | LogUtil.e(msg, tag: tag); 28 | } 29 | } 30 | 31 | static json(String msg, {tag: tag}) { 32 | if (!Constant.inProduction) { 33 | var data = convert.json.decode(msg); 34 | if (data is Map) { 35 | _printMap(data); 36 | } else if (data is List) { 37 | _printList(data); 38 | } else 39 | LogUtil.v(msg, tag: tag); 40 | } 41 | } 42 | 43 | // https://github.com/Milad-Akarie/pretty_dio_logger 44 | static void _printMap(Map data, {tag: tag, int tabs = 1, bool isListItem = false, bool isLast = false}) { 45 | final bool isRoot = tabs == 1; 46 | final initialIndent = _indent(tabs); 47 | tabs++; 48 | 49 | if (isRoot || isListItem) LogUtil.v('$initialIndent{', tag: tag); 50 | 51 | data.keys.toList().asMap().forEach((index, key) { 52 | final isLast = index == data.length - 1; 53 | var value = data[key]; 54 | if (value is String) value = '\"$value\"'; 55 | if (value is Map) { 56 | if (value.length == 0) 57 | LogUtil.v('${_indent(tabs)} $key: $value${!isLast ? ',' : ''}', tag: tag); 58 | else { 59 | LogUtil.v('${_indent(tabs)} $key: {', tag: tag); 60 | _printMap(value, tabs: tabs); 61 | } 62 | } else if (value is List) { 63 | if (value.length == 0) { 64 | LogUtil.v('${_indent(tabs)} $key: ${value.toString()}', tag: tag); 65 | } else { 66 | LogUtil.v('${_indent(tabs)} $key: [', tag: tag); 67 | _printList(value, tabs: tabs); 68 | LogUtil.v('${_indent(tabs)} ]${isLast ? '' : ','}', tag: tag); 69 | } 70 | } else { 71 | final msg = value.toString().replaceAll('\n', ''); 72 | LogUtil.v('${_indent(tabs)} $key: $msg${!isLast ? ',' : ''}', tag: tag); 73 | } 74 | }); 75 | 76 | LogUtil.v('$initialIndent}${isListItem && !isLast ? ',' : ''}', tag: tag); 77 | } 78 | 79 | static void _printList(List list, {tag: tag, int tabs = 1}) { 80 | list.asMap().forEach((i, e) { 81 | final isLast = i == list.length - 1; 82 | if (e is Map) { 83 | if (e.length == 0) 84 | LogUtil.v('${_indent(tabs)} $e${!isLast ? ',' : ''}', tag: tag); 85 | else 86 | _printMap(e, tabs: tabs + 1, isListItem: true, isLast: isLast); 87 | } else 88 | LogUtil.v('${_indent(tabs + 2)} $e${isLast ? '' : ','}', tag: tag); 89 | }); 90 | } 91 | 92 | static String _indent([int tabCount = 1]) => ' ' * tabCount; 93 | } -------------------------------------------------------------------------------- /lib/utils/native_method.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | 3 | /// *@author 何晏波 4 | /// *@QQ 1054539528 5 | /// *@date 2020-01-15 6 | /// *@Description: 原生端加密 7 | const encodePlugin = const MethodChannel('www.guigug.com/rsa_and_base64'); 8 | 9 | Future encode(String plaint) async { 10 | //获取到插件与原生的交互通道 11 | String result = await encodePlugin.invokeMethod('encode', {"plaint": plaint}); 12 | print('加密结果:' + result); 13 | return result; 14 | } 15 | 16 | /// *@author 何晏波 17 | /// *@QQ 1054539528 18 | /// *@date 2020-01-15 19 | /// *@Description: 加入随机因子 20 | String wrapWithTimestamps(String text) { 21 | return '$text@${new DateTime.now().millisecondsSinceEpoch}'; 22 | } 23 | -------------------------------------------------------------------------------- /lib/utils/net/base_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_app/models/entity_factory.dart'; 2 | 3 | import '../common.dart'; 4 | 5 | class BaseEntity{ 6 | 7 | int code; 8 | String message; 9 | T data; 10 | List listData = []; 11 | 12 | BaseEntity(this.code, this.message, this.data); 13 | 14 | BaseEntity.fromJson(Map json) { 15 | code = json[Constant.code]; 16 | message = json[Constant.message]; 17 | if (json.containsKey(Constant.data)) { 18 | if (json[Constant.data] is List) { 19 | (json[Constant.data] as List).forEach((item) { 20 | listData.add(_generateOBJ(item)); 21 | }); 22 | } else { 23 | data = _generateOBJ(json[Constant.data]); 24 | } 25 | } 26 | } 27 | 28 | S _generateOBJ(json) { 29 | if (S.toString() == "String") { 30 | return json.toString() as S; 31 | } else if (T.toString() == "Map") { 32 | return json as S; 33 | } else { 34 | return EntityFactory.generateOBJ(json); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /lib/utils/net/error_handle.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:dio/dio.dart'; 4 | 5 | class ExceptionHandle { 6 | static const int success = 200; 7 | static const int success_not_content = 204; 8 | static const int unauthorized = 401; 9 | static const int forbidden = 403; 10 | static const int not_found = 404; 11 | 12 | static const int net_error = 1000; 13 | static const int parse_error = 1001; 14 | static const int socket_error = 1002; 15 | static const int http_error = 1003; 16 | static const int timeout_error = 1004; 17 | static const int cancel_error = 1005; 18 | static const int unknown_error = 9999; 19 | 20 | static NetError handleException(dynamic error) { 21 | print(error); 22 | if (error is DioError) { 23 | if (error.type == DioErrorType.DEFAULT || 24 | error.type == DioErrorType.RESPONSE) { 25 | dynamic e = error.error; 26 | if (e is SocketException) { 27 | return NetError(socket_error, "网络异常,请检查你的网络!"); 28 | } 29 | if (e is HttpException) { 30 | return NetError(http_error, "服务器异常!"); 31 | } 32 | return NetError(net_error, "网络异常,请检查你的网络!"); 33 | } else if (error.type == DioErrorType.CONNECT_TIMEOUT || 34 | error.type == DioErrorType.SEND_TIMEOUT || 35 | error.type == DioErrorType.RECEIVE_TIMEOUT) { 36 | return NetError(timeout_error, "连接超时!"); 37 | } else if (error.type == DioErrorType.CANCEL) { 38 | return NetError(cancel_error, "取消请求"); 39 | } else { 40 | return NetError(unknown_error, "未知异常"); 41 | } 42 | } else { 43 | return NetError(unknown_error, "未知异常"); 44 | } 45 | } 46 | } 47 | 48 | class NetError{ 49 | int code; 50 | String msg; 51 | 52 | NetError(this.code, this.msg); 53 | } -------------------------------------------------------------------------------- /lib/utils/net/http_api.dart: -------------------------------------------------------------------------------- 1 | class HttpApi { 2 | /**登录模块**/ 3 | 4 | ///密码登录 5 | static const String password = 'login/password'; 6 | 7 | /**首页模块**/ 8 | 9 | ///获取首页板块配置数据 10 | static const String getHomePagePlate = 'plate/home'; 11 | 12 | ///获取素材详情数据 13 | static const String getMaterialDetail = 'material/detail'; 14 | 15 | /**云学术模块**/ 16 | 17 | ///获取云学术板块配置数据 18 | static const String cloudAcademy = 'plate/cloudAcademy'; 19 | 20 | /**专区模块**/ 21 | 22 | ///获取专区板块配置数据 23 | static const String special = 'plate/special'; 24 | 25 | /**用户模块**/ 26 | 27 | ///获取用户基本资料 28 | static const String getUserInfo = 'info/selfNew'; 29 | 30 | ///获取我的收藏记录 31 | static const String getCollectRecord = 'collect/pageRecord'; 32 | } 33 | -------------------------------------------------------------------------------- /lib/utils/net/net.dart: -------------------------------------------------------------------------------- 1 | export 'error_handle.dart'; 2 | export 'dio_utils.dart'; 3 | export 'http_api.dart'; 4 | -------------------------------------------------------------------------------- /lib/utils/progress.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_app/res/colors.dart'; 3 | import 'package:flutter_app/utils/routes/fluro_navigator.dart'; 4 | import 'package:flutter_app/utils/screen_util.dart'; 5 | import 'package:flutter_app/widgets/load_image.dart'; 6 | import 'image_utils.dart'; 7 | 8 | /// *@filename qiandao_alert.dart 9 | /// *@author 何晏波 10 | /// *@QQ 1054539528 11 | /// *@date 2020-01-15 12 | /// *@Description: 自定义进度展示对话框 13 | bool _isShowDialog = false; 14 | 15 | void showProgress(BuildContext context) async { 16 | if (!_isShowDialog) { 17 | _isShowDialog = true; 18 | await showGeneralDialog( 19 | context: context, 20 | barrierColor: Color(0x11000000), 21 | barrierDismissible: true, 22 | barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, 23 | transitionDuration: const Duration(milliseconds: 150), 24 | transitionBuilder: _buildMaterialDialogTransitions, 25 | pageBuilder: (BuildContext buildContext, Animation animation, 26 | Animation secondaryAnimation) { 27 | return SafeArea( 28 | child: Builder(builder: (BuildContext context) { 29 | return Center( 30 | child: Container( 31 | width: scaleSize(120), 32 | height: scaleSize(120), 33 | decoration: BoxDecoration( 34 | image: DecorationImage( 35 | image: ImageUtils.getAssetImage('ico_loading_bg'), 36 | fit: BoxFit.fill), 37 | ), 38 | child: Container( 39 | margin: EdgeInsets.only(top: scaleSize(25)), 40 | child: Column( 41 | children: [ 42 | LoadImage( 43 | 'loading', 44 | format: 'gif', 45 | width: scaleSize(100), 46 | height: scaleSize(40), 47 | ), 48 | Text( 49 | '加载中...', 50 | style: TextStyle( 51 | decoration: TextDecoration.none, 52 | fontSize: setSp(14), 53 | color: MyColors.textGrayColor, 54 | fontWeight: FontWeight.normal, 55 | ), 56 | ) 57 | ], 58 | ), 59 | ), 60 | ), 61 | ); 62 | }), 63 | ); 64 | }, 65 | ); 66 | _isShowDialog = false; 67 | } 68 | } 69 | 70 | void hideProgress(BuildContext context) { 71 | if (_isShowDialog) { 72 | _isShowDialog = false; 73 | NavigatorUtils.goBack(context); 74 | } 75 | } 76 | 77 | Widget _buildMaterialDialogTransitions( 78 | BuildContext context, 79 | Animation animation, 80 | Animation secondaryAnimation, 81 | Widget child) { 82 | return FadeTransition( 83 | opacity: CurvedAnimation( 84 | parent: animation, 85 | curve: Curves.easeOut, 86 | ), 87 | child: child, 88 | ); 89 | } 90 | -------------------------------------------------------------------------------- /lib/utils/routes/404.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_app/widgets/app_bar.dart'; 3 | import 'package:flutter_app/widgets/state_layout.dart'; 4 | 5 | class WidgetNotFound extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | return const Scaffold( 9 | appBar: MyAppBar( 10 | centerTitle: "页面不存在", 11 | ), 12 | body: Text("页面不存在"), 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/utils/routes/application.dart: -------------------------------------------------------------------------------- 1 | import 'package:fluro/fluro.dart'; 2 | 3 | class Application { 4 | static Router router; 5 | 6 | } 7 | -------------------------------------------------------------------------------- /lib/utils/routes/fluro_navigator.dart: -------------------------------------------------------------------------------- 1 | import 'package:fluro/fluro.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'application.dart'; 4 | import 'routers.dart'; 5 | 6 | /// fluro的路由跳转工具类 7 | class NavigatorUtils { 8 | 9 | static push(BuildContext context, String path, 10 | {bool replace = false, bool clearStack = false}) { 11 | FocusScope.of(context).unfocus(); 12 | Application.router.navigateTo(context, path, replace: replace, clearStack: clearStack, transition: TransitionType.cupertino); 13 | } 14 | 15 | static pushResult(BuildContext context, String path, Function(Object) function, 16 | {bool replace = false, bool clearStack = false}) { 17 | FocusScope.of(context).unfocus(); 18 | Application.router.navigateTo(context, path, replace: replace, clearStack: clearStack, transition: TransitionType.cupertino).then((result) { 19 | // 页面返回result为null 20 | if (result == null) { 21 | return; 22 | } 23 | function(result); 24 | }).catchError((error) { 25 | print("$error"); 26 | }); 27 | } 28 | 29 | /// 返回 30 | static void goBack(BuildContext context) { 31 | FocusScope.of(context).unfocus(); 32 | Navigator.pop(context); 33 | } 34 | 35 | /// 带参数返回 36 | static void goBackWithParams(BuildContext context, result) { 37 | FocusScope.of(context).unfocus(); 38 | Navigator.pop(context, result); 39 | } 40 | 41 | /// 跳到WebView页 42 | static goWebViewPage(BuildContext context, String title, String url) { 43 | //fluro 不支持传中文,需转换 44 | push(context, '${Routes.webViewPage}?title=${Uri.encodeComponent(title)}&url=${Uri.encodeComponent(url)}'); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/utils/routes/router_init.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:fluro/fluro.dart'; 3 | 4 | abstract class IRouterProvider{ 5 | 6 | void initRouter(Router router); 7 | } -------------------------------------------------------------------------------- /lib/utils/routes/routers.dart: -------------------------------------------------------------------------------- 1 | import 'package:fish_redux/fish_redux.dart'; 2 | import 'package:fluro/fluro.dart'; 3 | import 'package:flutter/material.dart' hide Action; 4 | import 'package:flutter_app/global_store/state.dart'; 5 | import 'package:flutter_app/global_store/store.dart'; 6 | import 'package:flutter_app/models/index/page_component.dart'; 7 | import 'package:flutter_app/routes/home_module/news_detail/page.dart'; 8 | import 'package:flutter_app/routes/home_module/news_detail/state.dart'; 9 | import 'package:flutter_app/routes/login_page/page.dart'; 10 | import 'package:flutter_app/routes/mine_module/collect_page/page.dart'; 11 | import 'package:flutter_app/routes/mine_module/setting_page/page.dart'; 12 | 13 | import '404.dart'; 14 | 15 | class Routes { 16 | //登录页面 17 | static String loginPage = "/loginPage"; 18 | 19 | //设置页面 20 | static String settingPage = "/settingPage"; 21 | 22 | //我的收藏页面 23 | static String collectPage = "/collectPage"; 24 | 25 | //新闻详情页面 26 | static String newsDetailPage = "/newsDetailPage"; 27 | static String webViewPage = "/webViewPage"; 28 | static List listRoutes = []; 29 | 30 | static void configureRoutes(Router router) { 31 | /// 指定路由跳转错误返回页 32 | router.notFoundHandler = Handler( 33 | handlerFunc: (BuildContext context, Map> params) { 34 | debugPrint("未找到目标页"); 35 | return WidgetNotFound(); 36 | }); 37 | 38 | listRoutes 39 | .add(PageComponent(pageName: Routes.loginPage, page: LoginPage())); 40 | 41 | listRoutes 42 | .add(PageComponent(pageName: Routes.settingPage, page: SettingPage())); 43 | 44 | listRoutes 45 | .add(PageComponent(pageName: Routes.collectPage, page: CollectPage())); 46 | 47 | router.define(Routes.newsDetailPage, handler: Handler(handlerFunc: (_, params) { 48 | return NewsDetailPage().buildPage(NewsInfoModel(title: params['title'].first,id: params['id'].first)); 49 | })); 50 | 51 | ///添加公共的方法 52 | listRoutes.forEach((dynamic route) { 53 | /// 只有特定的范围的 Page 才需要建立和 AppStore 的连接关系 54 | /// 满足 Page ,T 是 GlobalBaseState 的子类 55 | if (route.page.isTypeof()) { 56 | /// 建立 AppStore 驱动 PageStore 的单向数据连接 57 | /// 1. 参数1 AppStore 58 | /// 2. 参数2 当 AppStore.state 变化时, PageStore.state 该如何变化 59 | route.page.connectExtraStore(GlobalStore.store, 60 | (Object pagestate, GlobalState appState) { 61 | final GlobalBaseState p = pagestate; 62 | if (p.userInfo != appState.userInfo) { 63 | if (pagestate is Cloneable) { 64 | final Object copy = pagestate.clone(); 65 | final GlobalBaseState newState = copy; 66 | newState.userInfo = appState.userInfo; 67 | return newState; 68 | } 69 | } 70 | return pagestate; 71 | }); 72 | } 73 | 74 | route.page 75 | ..enhancer.append( 76 | /// View AOP 77 | viewMiddleware: >[ 78 | safetyView(), 79 | ], 80 | 81 | /// Adapter AOP 82 | adapterMiddleware: >[ 83 | safetyAdapter() 84 | ], 85 | 86 | /// Effect AOP 87 | effectMiddleware: >[ 88 | _pageAnalyticsMiddleware(), 89 | ], 90 | 91 | /// Store AOP 92 | middleware: >[ 93 | logMiddleware(tag: route.page.runtimeType.toString()), 94 | ], 95 | ); 96 | router.define(route.pageName, 97 | handler: Handler( 98 | handlerFunc: 99 | (BuildContext context, Map> params) => 100 | route.page.buildPage(null))); 101 | }); 102 | } 103 | } 104 | 105 | /// 简单的 Effect AOP 106 | /// 只针对页面的生命周期进行打印 107 | EffectMiddleware _pageAnalyticsMiddleware({String tag = 'redux'}) { 108 | return (AbstractLogic logic, Store store) { 109 | return (Effect effect) { 110 | return (Action action, Context ctx) { 111 | if (logic is Page && action.type is Lifecycle) { 112 | print('${logic.runtimeType} ${action.type.toString()} '); 113 | } 114 | return effect?.call(action, ctx); 115 | }; 116 | }; 117 | }; 118 | } 119 | -------------------------------------------------------------------------------- /lib/utils/routes/webview_page.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:async'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_app/widgets/app_bar.dart'; 5 | import 'package:webview_flutter/webview_flutter.dart'; 6 | 7 | class WebViewPage extends StatefulWidget { 8 | 9 | const WebViewPage({ 10 | Key key, 11 | @required this.title, 12 | @required this.url, 13 | }) : super(key: key); 14 | 15 | final String title; 16 | final String url; 17 | 18 | @override 19 | _WebViewPageState createState() => _WebViewPageState(); 20 | } 21 | 22 | class _WebViewPageState extends State { 23 | 24 | final Completer _controller = Completer(); 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return FutureBuilder( 29 | future: _controller.future, 30 | builder: (context, snapshot) { 31 | return WillPopScope( 32 | onWillPop: () async { 33 | if (snapshot.hasData) { 34 | bool canGoBack = await snapshot.data.canGoBack(); 35 | if (canGoBack) { 36 | // 网页可以返回时,优先返回上一页 37 | snapshot.data.goBack(); 38 | return Future.value(false); 39 | } 40 | return Future.value(true); 41 | } 42 | return Future.value(true); 43 | }, 44 | child: Scaffold( 45 | appBar: MyAppBar( 46 | centerTitle: widget.title, 47 | ), 48 | body: WebView( 49 | initialUrl: widget.url, 50 | javascriptMode: JavascriptMode.unrestricted, 51 | onWebViewCreated: (WebViewController webViewController) { 52 | _controller.complete(webViewController); 53 | }, 54 | ) 55 | ), 56 | ); 57 | } 58 | ); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /lib/utils/screen_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// *@filename screen_util.dart 4 | /// *@author 何晏波 5 | /// *@QQ 1054539528 6 | /// *@date 2020-01-03 7 | /// *@Description: 屏幕适配工具类 8 | class ScreenUtil { 9 | static ScreenUtil instance = new ScreenUtil(); 10 | 11 | /// UI设计中手机尺寸 , px 12 | /// Size of the phone in UI Design , px 13 | double width; 14 | double height; 15 | 16 | /// 控制字体是否要根据系统的“字体大小”辅助选项来进行缩放。默认值为false。 17 | /// allowFontScaling Specifies whether fonts should scale to respect Text Size accessibility settings. The default is false. 18 | bool allowFontScaling; 19 | 20 | static MediaQueryData _mediaQueryData; 21 | static double _screenWidth; 22 | static double _screenHeight; 23 | static double _pixelRatio; 24 | static double _statusBarHeight; 25 | 26 | static double _bottomBarHeight; 27 | 28 | static double _textScaleFactor; 29 | 30 | ScreenUtil({ 31 | this.width = 750, 32 | this.height = 1334, 33 | this.allowFontScaling = false, 34 | }); 35 | 36 | static ScreenUtil getInstance() { 37 | return instance; 38 | } 39 | 40 | void init(BuildContext context) { 41 | MediaQueryData mediaQuery = MediaQuery.of(context); 42 | _mediaQueryData = mediaQuery; 43 | _pixelRatio = mediaQuery.devicePixelRatio; 44 | _screenWidth = mediaQuery.size.width; 45 | _screenHeight = mediaQuery.size.height; 46 | _statusBarHeight = mediaQuery.padding.top; 47 | _bottomBarHeight = _mediaQueryData.padding.bottom; 48 | _textScaleFactor = mediaQuery.textScaleFactor; 49 | } 50 | 51 | static MediaQueryData get mediaQueryData => _mediaQueryData; 52 | 53 | /// 每个逻辑像素的字体像素数,字体的缩放比例 54 | /// The number of font pixels for each logical pixel. 55 | static double get textScaleFactor => _textScaleFactor; 56 | 57 | /// 设备的像素密度 58 | /// The size of the media in logical pixels (e.g, the size of the screen). 59 | static double get pixelRatio => _pixelRatio; 60 | 61 | /// 当前设备宽度 dp 62 | /// The horizontal extent of this size. 63 | static double get screenWidthDp => _screenWidth; 64 | 65 | ///当前设备高度 dp 66 | ///The vertical extent of this size. dp 67 | static double get screenHeightDp => _screenHeight; 68 | 69 | /// 当前设备宽度 px 70 | /// The vertical extent of this size. px 71 | static double get screenWidth => _screenWidth * _pixelRatio; 72 | 73 | /// 当前设备高度 px 74 | /// The vertical extent of this size. px 75 | static double get screenHeight => _screenHeight * _pixelRatio; 76 | 77 | /// 状态栏高度 dp 刘海屏会更高 78 | /// The offset from the top 79 | static double get statusBarHeight => _statusBarHeight; 80 | 81 | /// 底部安全区距离 dp 82 | /// The offset from the bottom. 83 | static double get bottomBarHeight => _bottomBarHeight; 84 | 85 | /// 实际的dp与UI设计px的比例 86 | /// The ratio of the actual dp to the design draft px 87 | double get scaleWidth => _screenWidth / instance.width; 88 | 89 | double get scaleHeight => _screenHeight / instance.height; 90 | 91 | /// 根据UI设计的设备宽度适配 92 | /// 高度也可以根据这个来做适配可以保证不变形,比如你先要一个正方形的时候. 93 | /// Adapted to the device width of the UI Design. 94 | /// Height can also be adapted according to this to ensure no deformation , 95 | /// if you want a square 96 | num setWidth(num width) => width * scaleWidth; 97 | 98 | /// 根据UI设计的设备高度适配 99 | /// 当发现UI设计中的一屏显示的与当前样式效果不符合时, 100 | /// 或者形状有差异时,建议使用此方法实现高度适配. 101 | /// 高度适配主要针对想根据UI设计的一屏展示一样的效果 102 | /// Highly adaptable to the device according to UI Design 103 | /// It is recommended to use this method to achieve a high degree of adaptation 104 | /// when it is found that one screen in the UI design 105 | /// does not match the current style effect, or if there is a difference in shape. 106 | num setHeight(num height) => height * scaleHeight; 107 | } 108 | 109 | num scaleSize(num width) => width * ScreenUtil.instance.scaleWidth * 2; 110 | 111 | ///字体大小适配方法 112 | ///@param [fontSize] UI设计上字体的大小,单位px. 113 | ///Font size adaptation method 114 | ///@param [fontSize] The size of the font on the UI design, in px. 115 | ///@param [allowFontScaling] 116 | num setSp(num fontSize) => ScreenUtil.instance.allowFontScaling 117 | ? scaleSize(fontSize) 118 | : scaleSize(fontSize) / ScreenUtil.textScaleFactor; 119 | 120 | void initScreenUtil(BuildContext context) { 121 | ScreenUtil.instance = ScreenUtil.getInstance()..init(context); 122 | } 123 | -------------------------------------------------------------------------------- /lib/utils/theme_utils.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_app/res/colors.dart'; 5 | 6 | class ThemeUtils { 7 | 8 | static bool isDark(BuildContext context) { 9 | return Theme.of(context).brightness == Brightness.dark; 10 | } 11 | 12 | static Color getDarkColor(BuildContext context, Color darkColor) { 13 | return isDark(context) ? darkColor : null; 14 | } 15 | 16 | static Color getIconColor(BuildContext context) { 17 | return isDark(context) ? MyColors.dark_text : null; 18 | } 19 | 20 | static Color getBackgroundColor(BuildContext context) { 21 | return Theme.of(context).scaffoldBackgroundColor; 22 | } 23 | 24 | static Color getDialogBackgroundColor(BuildContext context) { 25 | return Theme.of(context).canvasColor; 26 | } 27 | 28 | static Color getStickyHeaderColor(BuildContext context) { 29 | return isDark(context) ? MyColors.dark_bg_gray_ : MyColors.bg_gray_; 30 | } 31 | 32 | static Color getDialogTextFieldColor(BuildContext context) { 33 | return isDark(context) ? MyColors.dark_bg_gray_ : MyColors.bg_gray; 34 | } 35 | 36 | static Color getKeyboardActionsColor(BuildContext context) { 37 | return isDark(context) ? MyColors.dark_bg_color : Colors.grey[200]; 38 | } 39 | } -------------------------------------------------------------------------------- /lib/utils/toast.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:oktoast/oktoast.dart'; 3 | 4 | /// Toast工具类 5 | class Toast { 6 | static show(String msg, {duration = 2000}) { 7 | if (msg == null) { 8 | return; 9 | } 10 | showToast( 11 | msg, 12 | duration: Duration(milliseconds: duration), 13 | dismissOtherToast: true, 14 | position: ToastPosition.bottom, 15 | backgroundColor: Color.fromARGB(140, 0, 0, 0), 16 | radius: 20 17 | ); 18 | } 19 | 20 | static cancelToast() { 21 | dismissAllToast(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/widgets/app_bar.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_app/res/colors.dart'; 5 | import 'package:flutter_app/res/dimens.dart'; 6 | import 'package:flutter_app/res/gaps.dart'; 7 | import 'package:flutter_app/utils/theme_utils.dart'; 8 | 9 | /// 自定义AppBar 10 | class MyAppBar extends StatelessWidget implements PreferredSizeWidget{ 11 | 12 | const MyAppBar({ 13 | Key key, 14 | this.backgroundColor, 15 | this.title: "", 16 | this.centerTitle: "", 17 | this.actionName: "", 18 | this.backImg: "assets/images/ic_back_black.png", 19 | this.onPressed, 20 | this.isBack: true 21 | }): super(key: key); 22 | 23 | final Color backgroundColor; 24 | final String title; 25 | final String centerTitle; 26 | final String backImg; 27 | final String actionName; 28 | final VoidCallback onPressed; 29 | final bool isBack; 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | Color _backgroundColor; 34 | 35 | if (backgroundColor == null) { 36 | _backgroundColor = ThemeUtils.getBackgroundColor(context); 37 | } else { 38 | _backgroundColor = backgroundColor; 39 | } 40 | 41 | SystemUiOverlayStyle _overlayStyle = ThemeData.estimateBrightnessForColor(_backgroundColor) == Brightness.dark 42 | ? SystemUiOverlayStyle.light : SystemUiOverlayStyle.dark; 43 | return AnnotatedRegion( 44 | value: _overlayStyle, 45 | child: Material( 46 | color: _backgroundColor, 47 | child: SafeArea( 48 | child: Stack( 49 | alignment: Alignment.centerLeft, 50 | children: [ 51 | Semantics( 52 | namesRoute: true, 53 | header: true, 54 | child: Container( 55 | alignment: centerTitle.isEmpty ? Alignment.centerLeft : Alignment.center, 56 | width: double.infinity, 57 | child: Text( 58 | title.isEmpty ? centerTitle : title, 59 | style: TextStyle( 60 | fontSize: Dimens.font_sp18, 61 | color: _overlayStyle == SystemUiOverlayStyle.light ? MyColors.dark_text : MyColors.text, 62 | ) 63 | ), 64 | margin: const EdgeInsets.symmetric(horizontal: 48.0), 65 | ), 66 | ), 67 | isBack ? IconButton( 68 | onPressed: () { 69 | FocusScope.of(context).unfocus(); 70 | Navigator.maybePop(context); 71 | }, 72 | tooltip: 'Back', 73 | padding: const EdgeInsets.all(12.0), 74 | icon: Image.asset( 75 | backImg, 76 | color: _overlayStyle == SystemUiOverlayStyle.light ? MyColors.dark_text : MyColors.text, 77 | ), 78 | ) : Gaps.empty, 79 | Positioned( 80 | right: 0.0, 81 | child: Theme( 82 | data: Theme.of(context).copyWith( 83 | buttonTheme: ButtonThemeData( 84 | padding: const EdgeInsets.symmetric(horizontal: 16.0), 85 | minWidth: 60.0, 86 | ) 87 | ), 88 | child: actionName.isEmpty ? Container() : 89 | FlatButton( 90 | child: Text(actionName, key: const Key('actionName')), 91 | textColor: _overlayStyle == SystemUiOverlayStyle.light ? MyColors.dark_text : MyColors.text, 92 | highlightColor: Colors.transparent, 93 | onPressed: onPressed, 94 | ), 95 | ), 96 | ), 97 | ], 98 | ), 99 | ), 100 | ), 101 | ); 102 | } 103 | 104 | @override 105 | Size get preferredSize => Size.fromHeight(48.0); 106 | } 107 | -------------------------------------------------------------------------------- /lib/widgets/keep_alive_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// 保持状态的包裹类 4 | class KeepAliveWidget extends StatefulWidget { 5 | final Widget child; 6 | 7 | const KeepAliveWidget({this.child}); 8 | 9 | @override 10 | State createState() => _KeepAliveState(); 11 | } 12 | 13 | class _KeepAliveState extends State 14 | with AutomaticKeepAliveClientMixin { 15 | @override 16 | Widget build(BuildContext context) { 17 | super.build(context); 18 | return widget.child; 19 | } 20 | 21 | @override 22 | // TODO: implement wantKeepAlive 23 | bool get wantKeepAlive => true; 24 | } 25 | 26 | Widget keepAliveWrapper(Widget child) => KeepAliveWidget(child: child,); 27 | -------------------------------------------------------------------------------- /lib/widgets/list_subtitle.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_app/utils/screen_util.dart'; 4 | 5 | import 'load_image.dart'; 6 | 7 | /// *@filename list_subtitle.dart 8 | /// *@author 何晏波 9 | /// *@QQ 1054539528 10 | /// *@date 2020-01-17 11 | /// *@Description: ListSubtitle 12 | class ListSubtitle extends StatelessWidget { 13 | final String title; 14 | final String subTitle; 15 | 16 | const ListSubtitle({Key key, @required this.title, this.subTitle}) 17 | : super(key: key); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | // TODO: implement build 22 | return Column( 23 | crossAxisAlignment: CrossAxisAlignment.center, 24 | children: [ 25 | Container( 26 | color: Colors.white, 27 | width: ScreenUtil.screenWidthDp, 28 | height: scaleSize(50), 29 | child: FlatButton( 30 | padding: EdgeInsets.only(left: scaleSize(15), right: scaleSize(15)), 31 | onPressed: () {}, 32 | child: Row( 33 | crossAxisAlignment: CrossAxisAlignment.center, 34 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 35 | children: [ 36 | Text( 37 | title, 38 | style: TextStyle( 39 | fontWeight: FontWeight.normal, 40 | fontSize: setSp(14), 41 | color: Color(0xff182222), 42 | decoration: TextDecoration.none), 43 | ), 44 | subTitle == null 45 | ? LoadImage( 46 | 'ico_minepage_item_arrow', 47 | width: scaleSize(6), 48 | height: scaleSize(10), 49 | ) 50 | : Text( 51 | subTitle, 52 | style: TextStyle( 53 | fontSize: setSp(13), color: Color(0xff979aa0)), 54 | ) 55 | ], 56 | ), 57 | ), 58 | ), 59 | Container( 60 | width: ScreenUtil.screenWidthDp - scaleSize(30), 61 | height: 1, 62 | color: Color(0xffe9eff4), 63 | ) 64 | ], 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/widgets/load_image.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:common_utils/common_utils.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_app/utils/image_utils.dart'; 5 | 6 | /// *@filename load_image.dart 7 | /// *@author 何晏波 8 | /// *@QQ 1054539528 9 | /// *@date 2020-01-03 10 | /// *@Description: 图片加载(支持本地与网络图片) 11 | class LoadImage extends StatelessWidget { 12 | 13 | const LoadImage(this.image, { 14 | Key key, 15 | this.width, 16 | this.height, 17 | this.fit: BoxFit.cover, 18 | this.format: "png", 19 | this.holderImg: "none" 20 | }): super(key: key); 21 | 22 | final String image; 23 | final double width; 24 | final double height; 25 | final BoxFit fit; 26 | final String format; 27 | final String holderImg; 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | if (TextUtil.isEmpty(image) || image == "null") { 32 | return LoadAssetImage(holderImg, 33 | height: height, 34 | width: width, 35 | fit: fit, 36 | format: format 37 | ); 38 | } else { 39 | if (image.startsWith("http")) { 40 | return CachedNetworkImage( 41 | imageUrl: image, 42 | placeholder: (context, url) => LoadAssetImage(holderImg, height: height, width: width, fit: fit), 43 | errorWidget: (context, url, error) => LoadAssetImage(holderImg, height: height, width: width, fit: fit), 44 | width: width, 45 | height: height, 46 | fit: fit, 47 | ); 48 | } else { 49 | return LoadAssetImage(image, 50 | height: height, 51 | width: width, 52 | fit: fit, 53 | format: format 54 | ); 55 | } 56 | } 57 | } 58 | } 59 | 60 | /// 加载本地资源图片 61 | class LoadAssetImage extends StatelessWidget { 62 | 63 | const LoadAssetImage(this.image, { 64 | Key key, 65 | this.width, 66 | this.height, 67 | this.fit, 68 | this.format: 'png', 69 | this.color 70 | }): super(key: key); 71 | 72 | final String image; 73 | final double width; 74 | final double height; 75 | final BoxFit fit; 76 | final String format; 77 | final Color color; 78 | 79 | @override 80 | Widget build(BuildContext context) { 81 | 82 | return Image.asset( 83 | ImageUtils.getImgPath(image, format: format), 84 | height: height, 85 | width: width, 86 | fit: fit, 87 | color: color, 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/widgets/state_layout.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_app/res/dimens.dart'; 5 | import 'package:flutter_app/res/gaps.dart'; 6 | import 'package:flutter_app/utils/image_utils.dart'; 7 | import 'package:flutter_app/utils/theme_utils.dart'; 8 | 9 | /// design/9暂无状态页面/index.html#artboard3 10 | class StateLayout extends StatefulWidget { 11 | 12 | const StateLayout({ 13 | Key key, 14 | @required this.type, 15 | this.hintText 16 | }):super(key: key); 17 | 18 | final StateType type; 19 | final String hintText; 20 | 21 | @override 22 | _StateLayoutState createState() => _StateLayoutState(); 23 | } 24 | 25 | class _StateLayoutState extends State { 26 | 27 | String _img; 28 | String _hintText; 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | switch (widget.type) { 33 | case StateType.loading: 34 | _img = ""; 35 | _hintText = ""; 36 | break; 37 | case StateType.empty: 38 | _img = ""; 39 | _hintText = ""; 40 | break; 41 | } 42 | return Container( 43 | width: double.infinity, 44 | child: Column( 45 | crossAxisAlignment: CrossAxisAlignment.center, 46 | mainAxisAlignment: MainAxisAlignment.center, 47 | children: [ 48 | widget.type == StateType.loading ? const CupertinoActivityIndicator(radius: 16.0) : 49 | (widget.type == StateType.empty ? Gaps.empty : 50 | Opacity( 51 | opacity: ThemeUtils.isDark(context) ? 0.5 : 1, 52 | child: Container( 53 | height: 120.0, 54 | width: 120.0, 55 | decoration: BoxDecoration( 56 | image: DecorationImage( 57 | image: ImageUtils.getAssetImage("state/$_img"), 58 | ), 59 | ), 60 | )) 61 | ), 62 | Gaps.vGap16, 63 | Text( 64 | widget.hintText ?? _hintText, 65 | style: Theme.of(context).textTheme.subtitle.copyWith(fontSize: Dimens.font_sp14), 66 | ), 67 | Gaps.vGap50, 68 | ], 69 | ), 70 | ); 71 | } 72 | } 73 | 74 | enum StateType { 75 | /// 加载中 76 | loading, 77 | /// 空 78 | empty 79 | } -------------------------------------------------------------------------------- /lib/widgets/top_header.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_app/utils/routes/fluro_navigator.dart'; 3 | import 'package:flutter_app/utils/screen_util.dart'; 4 | import 'load_image.dart'; 5 | 6 | /// *@filename top_header.dart 7 | /// *@author 何晏波 8 | /// *@QQ 1054539528 9 | /// *@date 2020-01-17 10 | /// *@Description: 导航栏顶部组件 11 | 12 | class TopHeader extends StatelessWidget { 13 | final String title; 14 | 15 | const TopHeader({Key key, @required this.title}) : super(key: key); 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | // TODO: implement build 20 | return SafeArea( 21 | child: Container( 22 | color: Colors.white, 23 | width: scaleSize(ScreenUtil.screenWidthDp), 24 | height: scaleSize(44), 25 | child: Row( 26 | crossAxisAlignment: CrossAxisAlignment.center, 27 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 28 | children: [ 29 | GestureDetector( 30 | onTap: () { 31 | NavigatorUtils.goBack(context); 32 | ///以下是返回参数 33 | // NavigatorUtils.goBackWithParams(context, '参数'); 34 | }, 35 | child: Container( 36 | padding: EdgeInsets.all(scaleSize(10)), 37 | child: LoadImage( 38 | 'ico_back', 39 | width: scaleSize(10), 40 | height: scaleSize(20), 41 | ), 42 | ), 43 | ), 44 | ConstrainedBox( 45 | constraints: BoxConstraints(maxWidth: scaleSize(220)), 46 | child: Text( 47 | title, 48 | style: TextStyle( 49 | fontSize: setSp(18), 50 | color: Color(0xff182222), 51 | fontWeight: FontWeight.normal, 52 | decoration: TextDecoration.none), 53 | maxLines: 1, 54 | overflow: TextOverflow.ellipsis, 55 | ), 56 | ), 57 | SizedBox( 58 | width: scaleSize(30), 59 | ) 60 | ], 61 | ), 62 | ), 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_app 2 | description: A new Flutter application. 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # In Android, build-name is used as versionName while build-number used as versionCode. 10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 12 | # Read more about iOS versioning at 13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 14 | version: 1.0.0+1 15 | 16 | environment: 17 | sdk: ">=2.1.0 <3.0.0" 18 | 19 | dependencies: 20 | flutter: 21 | sdk: flutter 22 | flutter_localizations: 23 | sdk: flutter 24 | 25 | # The following adds the Cupertino Icons font to your application. 26 | # Use with the CupertinoIcons class for iOS style icons. 27 | cupertino_icons: ^0.1.2 28 | # Toast插件 https://github.com/OpenFlutter/flutter_oktoast 29 | oktoast: ^2.3.0 30 | # 网络库 https://github.com/flutterchina/dio 31 | dio: 3.0.7 32 | # Dart 常用工具类库 https://github.com/Sky24n/common_utils 33 | common_utils: ^1.1.3 34 | # Flutter 轮播图 https://github.com/best-flutter/flutter_swiper 35 | flutter_swiper: ^1.1.6 36 | # 启动URL的插件 https://github.com/flutter/plugins/tree/master/packages/url_launcher 37 | url_launcher: 5.4.1 38 | # WebView插件 https://github.com/flutter/plugins/tree/master/packages/webview_flutter 39 | webview_flutter: 0.3.18+1 40 | # 处理键盘事件 https://github.com/diegoveloper/flutter_keyboard_actions 41 | keyboard_actions: ^2.1.2 42 | # 路由框架 https://github.com/theyakka/fluro 43 | fluro: ^1.5.1 44 | # 图片缓存 https://github.com/renefloor/flutter_cached_network_image 45 | cached_network_image: ^2.0.0-rc 46 | # 状态管理 https://github.com/rrousselGit/provider 47 | provider: ^3.2.0 48 | # Flutter版微信SDK https://github.com/OpenFlutter/fluwx 49 | fluwx: ^1.2.1+1 50 | # Flutter 阿里闲鱼redux数据流框架 https://github.com/alibaba/fish-redux 51 | fish_redux: ^0.3.1 52 | # 格式化String https://github.com/Naddiseo/dart-sprintf 53 | sprintf: ^4.0.2 54 | # https://github.com/ReactiveX/rxdart 55 | rxdart: ^0.22.5 56 | # Flutter 常用工具类库 https://github.com/Sky24n/flustars 57 | flustars: 0.2.6+1 58 | #下拉刷新与加载更多 https://github.com/xuelongqy/flutter_easyrefresh 59 | flutter_easyrefresh: ^2.0.8 60 | 61 | dev_dependencies: 62 | flutter_test: 63 | sdk: flutter 64 | json2entity: 1.0.6 65 | 66 | 67 | # For information on the generic Dart part of this file, see the 68 | # following page: https://dart.dev/tools/pub/pubspec 69 | 70 | # The following section is specific to Flutter. 71 | flutter: 72 | 73 | # The following line ensures that the Material Icons font is 74 | # included with your application, so that you can use the icons in 75 | # the material Icons class. 76 | uses-material-design: true 77 | assets: 78 | - assets/images/ 79 | - assets/images/2.0x/ 80 | - assets/images/3.0x/ 81 | # To add assets to your application, add an assets section, like this: 82 | # assets: 83 | # - images/a_dot_burr.jpeg 84 | # - images/a_dot_ham.jpeg 85 | 86 | # An image asset can refer to one or more resolution-specific "variants", see 87 | # https://flutter.dev/assets-and-images/#resolution-aware. 88 | 89 | # For details regarding adding assets from package dependencies, see 90 | # https://flutter.dev/assets-and-images/#from-packages 91 | 92 | # To add custom fonts to your application, add a fonts section here, 93 | # in this "flutter" section. Each entry in this list should have a 94 | # "family" key with the font family name, and a "fonts" key with a 95 | # list giving the asset and other descriptors for the font. For 96 | # example: 97 | # fonts: 98 | # - family: Schyler 99 | # fonts: 100 | # - asset: fonts/Schyler-Regular.ttf 101 | # - asset: fonts/Schyler-Italic.ttf 102 | # style: italic 103 | # - family: Trajan Pro 104 | # fonts: 105 | # - asset: fonts/TrajanPro.ttf 106 | # - asset: fonts/TrajanPro_Bold.ttf 107 | # weight: 700 108 | # 109 | # For details regarding fonts from package dependencies, 110 | # see https://flutter.dev/custom-fonts/#from-packages 111 | --------------------------------------------------------------------------------