├── .gitignore ├── .metadata ├── README.md ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── rjx │ │ │ │ └── xiechengwang_app │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable │ │ │ ├── common_ctrip_slashscreen.png │ │ │ └── launch_background.xml │ │ │ ├── layout │ │ │ └── launch_screen.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── common_ic_launcher.png │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── asr_plugin │ ├── .gitignore │ ├── build.gradle │ ├── consumer-rules.pro │ ├── libs │ │ └── bdasr_V3_20191210_81acdf5.jar │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── rjx │ │ │ └── asr_plugin │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ │ ├── README.txt │ │ │ ├── WakeUp.bin │ │ │ └── baidu_speech_grammar.bsg │ │ ├── java │ │ │ └── com │ │ │ │ └── rjx │ │ │ │ └── asr_plugin │ │ │ │ ├── AsrManager.java │ │ │ │ ├── AsrPlugin.java │ │ │ │ ├── OnAsrListener.java │ │ │ │ ├── RecogEventAdapter.java │ │ │ │ ├── RecogResult.java │ │ │ │ └── ResultStateful.java │ │ ├── jniLibs │ │ │ ├── arm64-v8a │ │ │ │ ├── libBaiduSpeechSDK.so │ │ │ │ └── libvad.dnn.so │ │ │ ├── x86 │ │ │ │ ├── libBaiduSpeechSDK.so │ │ │ │ └── libvad.dnn.so │ │ │ └── x86_64 │ │ │ │ ├── libBaiduSpeechSDK.so │ │ │ │ └── libvad.dnn.so │ │ └── res │ │ │ └── values │ │ │ └── strings.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── rjx │ │ └── asr_plugin │ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── key.properties ├── proguard-rules.pro ├── settings.gradle └── xiecheng.jks ├── app-release.apk ├── images ├── type_channelgroup.png ├── type_channellgs.png ├── type_channelplane.png ├── type_channeltrain.png ├── type_cruise.png ├── type_district.png ├── type_food.png ├── type_hotel.png ├── type_huodong.png ├── type_shop.png ├── type_sight.png ├── type_ticket.png └── type_travelgroup.png ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ └── contents.xcworkspacedata └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── dao │ ├── home_dao.dart │ ├── search_dao.dart │ ├── travel_dao.dart │ └── travel_tab_dao.dart ├── main.dart ├── model │ ├── common_model.dart │ ├── config_mdoel.dart │ ├── grid_nav_model.dart │ ├── home_model.dart │ ├── sales_box_model.dart │ ├── search_model.dart │ ├── travel_model.dart │ └── travel_tab_model.dart ├── navigator │ └── tab_navigator.dart ├── pages │ ├── home_page.dart │ ├── my_page.dart │ ├── search_page.dart │ ├── speak_page.dart │ ├── travel_page.dart │ └── travel_tab_page.dart ├── plugin │ └── asr_manager.dart ├── util │ └── navigator_util.dart └── widget │ ├── grid_nav.dart │ ├── loading_container.dart │ ├── local_nav.dart │ ├── sales_box.dart │ ├── search_bar.dart │ ├── sub_nav.dart │ └── webview.dart ├── pubspec.lock ├── pubspec.yaml ├── screenshot ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png ├── 7.png ├── alt_1.PNG ├── alt_2.PNG ├── alt_3.PNG ├── alt_4.PNG ├── alt_5.PNG ├── alt_6.PNG ├── alt_7.PNG └── download.png └── test └── widget_test.dart /.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 | .packages 28 | .pub-cache/ 29 | .pub/ 30 | /build/ 31 | 32 | # Web related 33 | lib/generated_plugin_registrant.dart 34 | 35 | # Exceptions to above rules. 36 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 37 | -------------------------------------------------------------------------------- /.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: 056f7244ef02947c37b2369fe542ea87af6e1173 8 | channel: master 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xiechengwang_app 2 | 3 | > 一款仿携程旅行 Flutter App [速学CrazyCodeBoy](https://coding.imooc.com/class/321.html) 4 | 5 | [**下载** ](https://github.com/raojianxiong/xiechengwang_app/blob/master/app-release.apk) 点击下载应用 6 | 7 | 扫描下载 8 | 9 | ![扫描下载](https://github.com/raojianxiong/xiechengwang_app/blob/master/screenshot/download.png) 10 | 11 | ## 目录 12 | 13 | * 主要页面,包括首页、搜索、旅拍和我的四个主页面 14 | 15 | * 依赖库 16 | 17 | * 实际效果 18 | 19 | 20 | ### 主要页面 21 | 22 | * 整体框架采用PageView + BottomNavigationBar ,每个页面的状态保存采用AutomaticKeepAliveClientMixin 23 | 24 | * 首页 25 | 26 | * 全面屏适配,体现在顶部搜索框距离状态栏的距离,项目内笔者采用的是 27 | 28 | ``` 29 | MediaQueryData.fromWindow(window).padding.top 30 | ``` 31 | 32 | 得到状态栏高度进行适配,当然也可以使用SafeArea来包裹页面。(使用了Scaffold的appbar与bottomNavigationBar是不需要适配的,因为Scaffold框架会自动帮我们完成这些适配工作) 33 | 34 | * 轮播图主要采用的是Swiper控件 35 | 36 | * 列表采用ListView控件,如果数据过多,需要上拉加载建议使用ListView的Builder方法进行服用View 37 | 38 | * 主页整体布局采用了Stack + MediaQuery.removePadding + RefreshIndicator + appBar 39 | 40 | * 通过对Container进行alpha设置实现appBar的颜色渐变 41 | 42 | * 搜索 43 | 44 | * 语音识别采用百度API,native接入百度语音识别API,这里需要注意build.gradle的设置,由于笔者是通过新建android模块,所以需要仿照主app的build.gradle对fltter引入,才能导入MethodChannel相关类。此处涉及Flutter与native通信,两端方法名需要一致。 45 | * 语音识别后自动跳转就行搜索,利用ListView显示数据,用到FractionallySizedBox控件撑满屏幕宽度,利用Expand设置权重,个人感觉Expand等价于LinearLayout,flex属性和weight属性类似 46 | 47 | * 旅拍 48 | 49 | * TabBar + Flexible+ TabBarView 50 | * RefreshIndicator + StaggeredGridView + Stack + Card + PhysicalModel 实现下拉刷新 上拉加载 51 | * 文字固定宽度 LimitedBox 52 | * 圆形图片使用 PhysicalModel 圆角设置为控件长/宽一半 53 | 54 | * 我的 55 | 56 | * WebView 57 | 58 | * 网页加载 59 | 60 | * 所有点击功能采用GestureDetector控件实现,网页使用WebView(利用FlutterWebviewPlugin控件自定义)控件加载 61 | * 当然也可以利用webview_flutter插件替代上述自定义WebView 62 | 63 | * 网络 64 | 65 | * 采用Http get和post请求,json解析 66 | 67 | * 接口在项目内 68 | 69 | 70 | ### 依赖库 71 | 72 | * flutter_swiper: ^1.1.4 73 | * http: ^0.12.0+4 74 | * flutter_webview_plugin: ^0.3.10+1 75 | * flutter_staggered_grid_view: ^0.3.0 76 | * flutter_splash_screen: ^0.1.0 77 | * [Flutter插件开发](https://flutterchina.club/developing-packages/) [Flutter插件库](https://pub.flutter-io.cn/packages/) 78 | 79 | 80 | 81 | ### 实际效果 82 | 83 | ![Flutter_Trip](https://github.com/raojianxiong/xiechengwang_app/blob/master/screenshot/alt_1.PNG) 84 | 85 | ![Flutter_Trip](https://github.com/raojianxiong/xiechengwang_app/blob/master/screenshot/alt_2.PNG) 86 | 87 | ![Flutter_Trip](https://github.com/raojianxiong/xiechengwang_app/blob/master/screenshot/alt_3.PNG) 88 | 89 | ![Flutter_Trip](https://github.com/raojianxiong/xiechengwang_app/blob/master/screenshot/alt_4.PNG) 90 | 91 | ![Flutter_Trip](https://github.com/raojianxiong/xiechengwang_app/blob/master/screenshot/alt_5.PNG) 92 | 93 | ![Flutter_Trip](https://github.com/raojianxiong/xiechengwang_app/blob/master/screenshot/alt_6.PNG) 94 | 95 | ![Flutter_Trip](https://github.com/raojianxiong/xiechengwang_app/blob/master/screenshot/alt_7.PNG) 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | def keystoreProperties = new Properties() 29 | def keystorePropertiesFile = rootProject.file('key.properties') 30 | if (keystorePropertiesFile.exists()) { 31 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 32 | } 33 | 34 | android { 35 | compileSdkVersion 28 36 | 37 | sourceSets { 38 | main.java.srcDirs += 'src/main/kotlin' 39 | } 40 | 41 | lintOptions { 42 | disable 'InvalidPackage' 43 | } 44 | 45 | defaultConfig { 46 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 47 | applicationId "com.rjx.xiechengwang_app" 48 | minSdkVersion 16 49 | targetSdkVersion 28 50 | versionCode flutterVersionCode.toInteger() 51 | versionName flutterVersionName 52 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 53 | ndk { 54 | abiFilters "arm64-v8a", "x86_64", "x86" //只打包flutter所支持的架构 55 | } 56 | } 57 | signingConfigs { 58 | release { 59 | keyAlias keystoreProperties['keyAlias'] 60 | keyPassword keystoreProperties['keyPassword'] 61 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 62 | storePassword keystoreProperties['storePassword'] 63 | } 64 | } 65 | buildTypes { 66 | release { 67 | // TODO: Add your own signing config for the release build. 68 | // Signing with the debug keys for now, so `flutter run --release` works. 69 | signingConfig signingConfigs.release 70 | minifyEnabled true 71 | 72 | } 73 | } 74 | //app 与 asr_plugin 都依赖于 libflutter.so merge时会冲突 75 | packagingOptions { 76 | 77 | pickFirst 'lib/x86-64/libflutter.so' 78 | pickFirst 'lib/x86/libflutter.so' 79 | pickFirst 'lib/arm64-v8a/libflutter.so' 80 | pickFirst 'lib/armeabi-v7a/libapp.so' 81 | pickFirst 'lib/arm64-v8a/libapp.so' 82 | } 83 | 84 | } 85 | flutter { 86 | source '../..' 87 | } 88 | 89 | dependencies { 90 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 91 | testImplementation 'junit:junit:4.12' 92 | androidTestImplementation 'androidx.test:runner:1.1.1' 93 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 94 | implementation project(':asr_plugin') 95 | 96 | } 97 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 15 | 22 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/rjx/xiechengwang_app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.rjx.xiechengwang_app 2 | 3 | import android.os.Bundle 4 | import com.rjx.asr_plugin.AsrPlugin 5 | 6 | import io.flutter.app.FlutterActivity 7 | import io.flutter.plugins.GeneratedPluginRegistrant 8 | import org.devio.flutter.splashscreen.SplashScreen 9 | 10 | class MainActivity : FlutterActivity() { 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | SplashScreen.show(this,true) 13 | super.onCreate(savedInstanceState) 14 | GeneratedPluginRegistrant.registerWith(this) 15 | registerSelfPlugin() 16 | } 17 | 18 | private fun registerSelfPlugin() { 19 | AsrPlugin.registerWith(registrarFor("com.rjx.asr_plugin.AsrPlugin")) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/common_ctrip_slashscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/android/app/src/main/res/drawable/common_ctrip_slashscreen.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/layout/launch_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/common_ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/android/app/src/main/res/mipmap-hdpi/common_ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #000000 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/asr_plugin/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /android/asr_plugin/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | def localProperties = new Properties() 4 | def localPropertiesFile = rootProject.file('local.properties') 5 | if (localPropertiesFile.exists()) { 6 | localPropertiesFile.withReader('UTF-8') { reader -> 7 | localProperties.load(reader) 8 | } 9 | } 10 | def flutterRoot = localProperties.getProperty('flutter.sdk') 11 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 12 | 13 | 14 | android { 15 | compileSdkVersion 28 16 | // buildToolsVersion "29.0.2" 17 | 18 | 19 | defaultConfig { 20 | minSdkVersion 16 21 | targetSdkVersion 28 22 | versionCode 1 23 | versionName "1.0" 24 | 25 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 26 | consumerProguardFiles 'consumer-rules.pro' 27 | } 28 | 29 | buildTypes { 30 | release { 31 | minifyEnabled false 32 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 33 | } 34 | } 35 | packagingOptions{ 36 | doNotStrip "*/*/libvad.dnn.so" 37 | 38 | } 39 | 40 | } 41 | flutter { 42 | source '../..' 43 | } 44 | dependencies { 45 | implementation fileTree(dir: 'libs', include: ['*.jar']) 46 | 47 | implementation 'androidx.appcompat:appcompat:1.0.2' 48 | testImplementation 'junit:junit:4.12' 49 | androidTestImplementation 'androidx.test.ext:junit:1.1.0' 50 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 51 | } 52 | -------------------------------------------------------------------------------- /android/asr_plugin/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/android/asr_plugin/consumer-rules.pro -------------------------------------------------------------------------------- /android/asr_plugin/libs/bdasr_V3_20191210_81acdf5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/android/asr_plugin/libs/bdasr_V3_20191210_81acdf5.jar -------------------------------------------------------------------------------- /android/asr_plugin/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -keep class com.baidu.speech.**{*;} 23 | -------------------------------------------------------------------------------- /android/asr_plugin/src/androidTest/java/com/rjx/asr_plugin/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.rjx.asr_plugin; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.rjx.asr_plugin.test", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /android/asr_plugin/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 21 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /android/asr_plugin/src/main/assets/README.txt: -------------------------------------------------------------------------------- 1 | 百度语音识别SDK在demo程序中assets目录的文件解释说明: 2 | WakeUp.bin 唤醒功能的唤醒词配置文件, 需要在百度语音开发平台中定义和导出 http://speech.baidu.com/wake 3 | baidu_speech_grammar.bsg 自定义语义以及离线命令词识别共用的语法文件, 需要在百度语音开发平台中定义和导出 http://speech.baidu.com/asr -------------------------------------------------------------------------------- /android/asr_plugin/src/main/assets/WakeUp.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/android/asr_plugin/src/main/assets/WakeUp.bin -------------------------------------------------------------------------------- /android/asr_plugin/src/main/assets/baidu_speech_grammar.bsg: -------------------------------------------------------------------------------- 1 | %7B%0A%20%20%20%20%22version%22%3A%20%220.1%22%2C%0A%20%20%20%20%22slots%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22name%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%E5%BC%A0%E4%B8%89%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%E6%9D%8E%E5%9B%9B%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%E7%8E%8B%E4%BA%94%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22appname%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%E5%BE%AE%E4%BF%A1%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%E7%9F%AD%E4%BF%A1%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%E8%AE%A1%E7%AE%97%E5%99%A8%22%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22msgbody%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22.%2B%22%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22rules%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22telephone.call%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22origin%22%3A%20%22%E6%89%93%E7%94%B5%E8%AF%9D%E7%BB%99%3Cname%3E%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22pattern%22%3A%20%22%5E%E6%89%93%E7%94%B5%E8%AF%9D%E7%BB%99(%E5%BC%A0%E4%B8%89%7C%E6%9D%8E%E5%9B%9B%7C%E7%8E%8B%E4%BA%94)%24%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22groups%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22name%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22origin%22%3A%20%22%E6%89%93%E7%BB%99%3Cname%3E%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22pattern%22%3A%20%22%5E%E6%89%93%E7%BB%99(%E5%BC%A0%E4%B8%89%7C%E6%9D%8E%E5%9B%9B%7C%E7%8E%8B%E4%BA%94)%24%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22groups%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22name%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22origin%22%3A%20%22%E5%91%BC%E5%8F%AB%3Cname%3E%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22pattern%22%3A%20%22%5E%E5%91%BC%E5%8F%AB(%E5%BC%A0%E4%B8%89%7C%E6%9D%8E%E5%9B%9B%7C%E7%8E%8B%E4%BA%94)%24%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22groups%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22name%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22origin%22%3A%20%22%E7%BB%99%3Cname%3E%E6%89%93%E7%94%B5%E8%AF%9D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22pattern%22%3A%20%22%5E%E7%BB%99(%E5%BC%A0%E4%B8%89%7C%E6%9D%8E%E5%9B%9B%7C%E7%8E%8B%E4%BA%94)%E6%89%93%E7%94%B5%E8%AF%9D%24%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22groups%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22name%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22contacts.view%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22origin%22%3A%20%22%E6%9F%A5%E7%9C%8B%3Cname%3E%E7%9A%84%E7%94%B5%E8%AF%9D%E5%8F%B7%E7%A0%81%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22pattern%22%3A%20%22%5E%E6%9F%A5%E7%9C%8B(%E5%BC%A0%E4%B8%89%7C%E6%9D%8E%E5%9B%9B%7C%E7%8E%8B%E4%BA%94)%E7%9A%84%E7%94%B5%E8%AF%9D%E5%8F%B7%E7%A0%81%24%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22groups%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22name%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22origin%22%3A%20%22%E6%9F%A5%E7%9C%8B%3Cname%3E%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22pattern%22%3A%20%22%5E%E6%9F%A5%E7%9C%8B(%E5%BC%A0%E4%B8%89%7C%E6%9D%8E%E5%9B%9B%7C%E7%8E%8B%E4%BA%94)%24%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22groups%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22name%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22contacts.create%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22origin%22%3A%20%22%E6%96%B0%E5%BB%BA%E8%81%94%E7%B3%BB%E4%BA%BA%3Cname%3E%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22pattern%22%3A%20%22%5E%E6%96%B0%E5%BB%BA%E8%81%94%E7%B3%BB%E4%BA%BA(%E5%BC%A0%E4%B8%89%7C%E6%9D%8E%E5%9B%9B%7C%E7%8E%8B%E4%BA%94)%24%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22groups%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22name%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22contacts.remove%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22origin%22%3A%20%22%E5%88%A0%E9%99%A4%3Cname%3E%E7%9A%84%E8%81%94%E7%B3%BB%E6%96%B9%E5%BC%8F%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22pattern%22%3A%20%22%5E%E5%88%A0%E9%99%A4(%E5%BC%A0%E4%B8%89%7C%E6%9D%8E%E5%9B%9B%7C%E7%8E%8B%E4%BA%94)%E7%9A%84%E8%81%94%E7%B3%BB%E6%96%B9%E5%BC%8F%24%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22groups%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22name%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22message.view%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22origin%22%3A%20%22%E6%9F%A5%E7%9C%8B%E6%9C%AA%E8%AF%BB%E7%9F%AD%E4%BF%A1%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22pattern%22%3A%20%22%5E%E6%9F%A5%E7%9C%8B%E6%9C%AA%E8%AF%BB%E7%9F%AD%E4%BF%A1%24%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22groups%22%3A%20%5B%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22message.send%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22origin%22%3A%20%22%E5%8F%91%E7%9F%AD%E4%BF%A1%E7%BB%99%3Cname%3E%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22pattern%22%3A%20%22%5E%E5%8F%91%E7%9F%AD%E4%BF%A1%E7%BB%99(%E5%BC%A0%E4%B8%89%7C%E6%9D%8E%E5%9B%9B%7C%E7%8E%8B%E4%BA%94)%24%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22groups%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22name%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22origin%22%3A%20%22%E5%8F%91%E7%9F%AD%E4%BF%A1%E7%BB%99%3Cname%3E%E5%86%85%E5%AE%B9%E6%98%AF%3Cmsgbody%3E%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22pattern%22%3A%20%22%5E%E5%8F%91%E7%9F%AD%E4%BF%A1%E7%BB%99(%E5%BC%A0%E4%B8%89%7C%E6%9D%8E%E5%9B%9B%7C%E7%8E%8B%E4%BA%94)%E5%86%85%E5%AE%B9%E6%98%AF(.%2B)%24%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22groups%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22name%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22msgbody%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22app.open%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22origin%22%3A%20%22%E6%89%93%E5%BC%80%3Cappname%3E%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22pattern%22%3A%20%22%5E%E6%89%93%E5%BC%80(%E5%BE%AE%E4%BF%A1%7C%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE%7C%E7%9F%AD%E4%BF%A1%7C%E8%AE%A1%E7%AE%97%E5%99%A8)%24%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22groups%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22appname%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22origin%22%3A%20%22%E5%90%AF%E5%8A%A8%3Cappname%3E%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22pattern%22%3A%20%22%5E%E5%90%AF%E5%8A%A8(%E5%BE%AE%E4%BF%A1%7C%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE%7C%E7%9F%AD%E4%BF%A1%7C%E8%AE%A1%E7%AE%97%E5%99%A8)%24%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22groups%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22appname%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22app.search%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22origin%22%3A%20%22%E6%90%9C%E7%B4%A2%3Cappname%3E%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22pattern%22%3A%20%22%5E%E6%90%9C%E7%B4%A2(%E5%BE%AE%E4%BF%A1%7C%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE%7C%E7%9F%AD%E4%BF%A1%7C%E8%AE%A1%E7%AE%97%E5%99%A8)%24%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22groups%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22appname%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22app.download%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22origin%22%3A%20%22%E4%B8%8B%E8%BD%BD%3Cappname%3E%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22pattern%22%3A%20%22%5E%E4%B8%8B%E8%BD%BD(%E5%BE%AE%E4%BF%A1%7C%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE%7C%E7%9F%AD%E4%BF%A1%7C%E8%AE%A1%E7%AE%97%E5%99%A8)%24%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22groups%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22appname%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22grammar%22%3A%20%22%3Cname%3E%20%3D%20%E5%BC%A0%E4%B8%89%7C%20%5Cn%E6%9D%8E%E5%9B%9B%7C%20%5Cn%E7%8E%8B%E4%BA%94%3B%5Cn%3Cappname%3E%20%3D%20%E5%BE%AE%E4%BF%A1%7C%20%5Cn%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE%7C%20%5Cn%E7%9F%AD%E4%BF%A1%7C%20%5Cn%E8%AE%A1%E7%AE%97%E5%99%A8%3B%5Cn%3Cmsgbody%3E%20%3D%20%E8%AF%8D%E6%9D%A1%E9%BB%98%E8%AE%A4%E5%80%BC%3B%5Cn%3Cauto_create_node%3E%20%3D%20%E6%9F%A5%E7%9C%8B%E6%9C%AA%E8%AF%BB%E7%9F%AD%E4%BF%A1%3B%5Cn%3C_wakeup%3E%20%3D%20%E5%94%A4%E9%86%92%E8%AF%8D%E5%8D%A0%E4%BD%8D%E7%AC%A6%3B%5Cn%5Cn%5Cn_SCENE_ID_%200%5Cn%5Cn(%20%3Cauto_create_node%3E%20)%5Cn%5Cn(%20%3C_wakeup%3E%3Cauto_create_node%3E%20)%5Cn%5Cn(%20%E6%89%93%E7%94%B5%E8%AF%9D%E7%BB%99%3Cname%3E%20)%5Cn(%20%3C_wakeup%3E%E6%89%93%E7%94%B5%E8%AF%9D%E7%BB%99%3Cname%3E%20)%5Cn(%20%E6%89%93%E7%BB%99%3Cname%3E%20)%5Cn(%20%3C_wakeup%3E%E6%89%93%E7%BB%99%3Cname%3E%20)%5Cn(%20%E5%91%BC%E5%8F%AB%3Cname%3E%20)%5Cn(%20%3C_wakeup%3E%E5%91%BC%E5%8F%AB%3Cname%3E%20)%5Cn(%20%E7%BB%99%3Cname%3E%E6%89%93%E7%94%B5%E8%AF%9D%20)%5Cn(%20%3C_wakeup%3E%E7%BB%99%3Cname%3E%E6%89%93%E7%94%B5%E8%AF%9D%20)%5Cn(%20%E6%9F%A5%E7%9C%8B%3Cname%3E%E7%9A%84%E7%94%B5%E8%AF%9D%E5%8F%B7%E7%A0%81%20)%5Cn(%20%3C_wakeup%3E%E6%9F%A5%E7%9C%8B%3Cname%3E%E7%9A%84%E7%94%B5%E8%AF%9D%E5%8F%B7%E7%A0%81%20)%5Cn(%20%E6%9F%A5%E7%9C%8B%3Cname%3E%20)%5Cn(%20%3C_wakeup%3E%E6%9F%A5%E7%9C%8B%3Cname%3E%20)%5Cn(%20%E6%96%B0%E5%BB%BA%E8%81%94%E7%B3%BB%E4%BA%BA%3Cname%3E%20)%5Cn(%20%3C_wakeup%3E%E6%96%B0%E5%BB%BA%E8%81%94%E7%B3%BB%E4%BA%BA%3Cname%3E%20)%5Cn(%20%E5%88%A0%E9%99%A4%3Cname%3E%E7%9A%84%E8%81%94%E7%B3%BB%E6%96%B9%E5%BC%8F%20)%5Cn(%20%3C_wakeup%3E%E5%88%A0%E9%99%A4%3Cname%3E%E7%9A%84%E8%81%94%E7%B3%BB%E6%96%B9%E5%BC%8F%20)%5Cn(%20%E5%8F%91%E7%9F%AD%E4%BF%A1%E7%BB%99%3Cname%3E%20)%5Cn(%20%3C_wakeup%3E%E5%8F%91%E7%9F%AD%E4%BF%A1%E7%BB%99%3Cname%3E%20)%5Cn(%20%E5%8F%91%E7%9F%AD%E4%BF%A1%E7%BB%99%3Cname%3E%E5%86%85%E5%AE%B9%E6%98%AF%3Cmsgbody%3E%20)%5Cn(%20%3C_wakeup%3E%E5%8F%91%E7%9F%AD%E4%BF%A1%E7%BB%99%3Cname%3E%E5%86%85%E5%AE%B9%E6%98%AF%3Cmsgbody%3E%20)%5Cn(%20%E6%89%93%E5%BC%80%3Cappname%3E%20)%5Cn(%20%3C_wakeup%3E%E6%89%93%E5%BC%80%3Cappname%3E%20)%5Cn(%20%E5%90%AF%E5%8A%A8%3Cappname%3E%20)%5Cn(%20%3C_wakeup%3E%E5%90%AF%E5%8A%A8%3Cappname%3E%20)%5Cn(%20%E6%90%9C%E7%B4%A2%3Cappname%3E%20)%5Cn(%20%3C_wakeup%3E%E6%90%9C%E7%B4%A2%3Cappname%3E%20)%5Cn(%20%E4%B8%8B%E8%BD%BD%3Cappname%3E%20)%5Cn(%20%3C_wakeup%3E%E4%B8%8B%E8%BD%BD%3Cappname%3E%20)%5Cn%22%2C%0A%20%20%20%20%22origin_slots%22%3A%20%22name%20%3D%20%E5%BC%A0%E4%B8%89%2C%20%E6%9D%8E%E5%9B%9B%2C%20%E7%8E%8B%E4%BA%94%5Cnappname%20%3D%20%E5%BE%AE%E4%BF%A1%2C%20%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9B%BE%2C%20%E7%9F%AD%E4%BF%A1%2C%20%E8%AE%A1%E7%AE%97%E5%99%A8%5Cnmsgbody%20%3D%20*%22%2C%0A%20%20%20%20%22origin_rules%22%3A%20%22telephone.call%20%20%20%20%20%3D%20%E6%89%93%E7%94%B5%E8%AF%9D%E7%BB%99%3Cname%3E%2C%20%E6%89%93%E7%BB%99%3Cname%3E%2C%20%E5%91%BC%E5%8F%AB%3Cname%3E%2C%E7%BB%99%3Cname%3E%E6%89%93%E7%94%B5%E8%AF%9D%5Cncontacts.view%20%20%20%20%20%3D%20%E6%9F%A5%E7%9C%8B%3Cname%3E%E7%9A%84%E7%94%B5%E8%AF%9D%E5%8F%B7%E7%A0%81%2C%20%E6%9F%A5%E7%9C%8B%3Cname%3E%5Cncontacts.create%20%20%3D%20%E6%96%B0%E5%BB%BA%E8%81%94%E7%B3%BB%E4%BA%BA%3Cname%3E%5Cncontacts.remove%3D%20%E5%88%A0%E9%99%A4%3Cname%3E%E7%9A%84%E8%81%94%E7%B3%BB%E6%96%B9%E5%BC%8F%5Cnmessage.view%20%20%20%20%20%3D%20%E6%9F%A5%E7%9C%8B%E6%9C%AA%E8%AF%BB%E7%9F%AD%E4%BF%A1%5Cnmessage.send%20%20%20%20%3D%20%E5%8F%91%E7%9F%AD%E4%BF%A1%E7%BB%99%3Cname%3E%2C%20%E5%8F%91%E7%9F%AD%E4%BF%A1%E7%BB%99%3Cname%3E%E5%86%85%E5%AE%B9%E6%98%AF%3Cmsgbody%3E%5Cnapp.open%20%20%20%20%20%20%20%20%20%20%20%20%3D%20%E6%89%93%E5%BC%80%3Cappname%3E%2C%20%E5%90%AF%E5%8A%A8%3Cappname%3E%5Cnapp.search%20%20%20%20%20%20%20%20%20%3D%20%E6%90%9C%E7%B4%A2%3Cappname%3E%5Cnapp.download%20%20%20%20%3D%20%E4%B8%8B%E8%BD%BD%3Cappname%3E%22%0A%7D -------------------------------------------------------------------------------- /android/asr_plugin/src/main/java/com/rjx/asr_plugin/AsrManager.java: -------------------------------------------------------------------------------- 1 | package com.rjx.asr_plugin; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import com.baidu.speech.EventListener; 7 | import com.baidu.speech.EventManager; 8 | import com.baidu.speech.EventManagerFactory; 9 | import com.baidu.speech.asr.SpeechConstant; 10 | import org.json.JSONObject; 11 | 12 | import java.util.Map; 13 | 14 | /** 15 | * Created by fujiayi on 2017/6/13. 16 | * EventManager内的方法如send 都可以在主线程中进行,SDK中做过处理 17 | */ 18 | 19 | public class AsrManager { 20 | /** 21 | * SDK 内部核心 EventManager 类 22 | */ 23 | private EventManager asr; 24 | 25 | // SDK 内部核心 事件回调类, 用于开发者写自己的识别回调逻辑 26 | private EventListener eventListener; 27 | 28 | // 是否加载离线资源 29 | private static boolean isOfflineEngineLoaded = false; 30 | 31 | // 未release前,只能new一个 32 | private static volatile boolean isInited = false; 33 | 34 | private static final String TAG = "AsrManager"; 35 | 36 | /** 37 | * 初始化 38 | * 39 | * @param context 40 | * @param recogListener 将EventListener结果做解析的DEMO回调。使用RecogEventAdapter 适配EventListener 41 | */ 42 | public AsrManager(Context context, OnAsrListener recogListener) { 43 | this(context, new RecogEventAdapter(recogListener)); 44 | } 45 | 46 | /** 47 | * 初始化 提供 EventManagerFactory需要的Context和EventListener 48 | * 49 | * @param context 50 | * @param eventListener 识别状态和结果回调 51 | */ 52 | public AsrManager(Context context, EventListener eventListener) { 53 | if (isInited) { 54 | Log.e(TAG, "还未调用release(),请勿新建一个新类"); 55 | throw new RuntimeException("还未调用release(),请勿新建一个新类"); 56 | } 57 | isInited = true; 58 | this.eventListener = eventListener; 59 | // SDK集成步骤 初始化asr的EventManager示例,多次得到的类,只能选一个使用 60 | asr = EventManagerFactory.create(context, "asr"); 61 | // SDK集成步骤 设置回调event, 识别引擎会回调这个类告知重要状态和识别结果 62 | asr.registerListener(eventListener); 63 | } 64 | 65 | 66 | /** 67 | * 离线命令词,在线不需要调用 68 | * 69 | * @param params 离线命令词加载参数,见文档“ASR_KWS_LOAD_ENGINE 输入事件参数” 70 | */ 71 | public void loadOfflineEngine(Map params) { 72 | String json = new JSONObject(params).toString(); 73 | Log.i(TAG + ".Debug", "离线命令词初始化参数(反馈请带上此行日志):" + json); 74 | // SDK集成步骤(可选)加载离线命令词(离线时使用) 75 | asr.send(SpeechConstant.ASR_KWS_LOAD_ENGINE, json, null, 0, 0); 76 | isOfflineEngineLoaded = true; 77 | } 78 | 79 | /** 80 | * @param params 81 | */ 82 | public void start(Map params) { 83 | if (!isInited) { 84 | throw new RuntimeException("release() was called"); 85 | } 86 | // SDK集成步骤 拼接识别参数 87 | String json = new JSONObject(params).toString(); 88 | Log.i(TAG + ".Debug", "识别参数(反馈请带上此行日志)" + json); 89 | asr.send(SpeechConstant.ASR_START, json, null, 0, 0); 90 | } 91 | 92 | 93 | /** 94 | * 提前结束录音等待识别结果。 95 | */ 96 | public void stop() { 97 | Log.i(TAG, "停止录音"); 98 | // SDK 集成步骤(可选)停止录音 99 | if (!isInited) { 100 | throw new RuntimeException("release() was called"); 101 | } 102 | asr.send(SpeechConstant.ASR_STOP, "{}", null, 0, 0); 103 | } 104 | 105 | /** 106 | * 取消本次识别,取消后将立即停止不会返回识别结果。 107 | * cancel 与stop的区别是 cancel在stop的基础上,完全停止整个识别流程, 108 | */ 109 | public void cancel() { 110 | Log.i(TAG, "取消识别"); 111 | if (!isInited) { 112 | throw new RuntimeException("release() was called"); 113 | } 114 | // SDK集成步骤 (可选) 取消本次识别 115 | asr.send(SpeechConstant.ASR_CANCEL, "{}", null, 0, 0); 116 | } 117 | 118 | public void release() { 119 | if (asr == null) { 120 | return; 121 | } 122 | cancel(); 123 | if (isOfflineEngineLoaded) { 124 | // SDK集成步骤 如果之前有调用过 加载离线命令词,这里要对应释放 125 | asr.send(SpeechConstant.ASR_KWS_UNLOAD_ENGINE, null, null, 0, 0); 126 | isOfflineEngineLoaded = false; 127 | } 128 | // SDK 集成步骤(可选),卸载listener 129 | asr.unregisterListener(eventListener); 130 | asr = null; 131 | isInited = false; 132 | } 133 | 134 | public void setEventListener(OnAsrListener recogListener) { 135 | if (!isInited) { 136 | throw new RuntimeException("release() was called"); 137 | } 138 | this.eventListener = new RecogEventAdapter(recogListener); 139 | asr.registerListener(eventListener); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /android/asr_plugin/src/main/java/com/rjx/asr_plugin/AsrPlugin.java: -------------------------------------------------------------------------------- 1 | package com.rjx.asr_plugin; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.pm.PackageManager; 6 | import android.util.Log; 7 | 8 | import androidx.annotation.Nullable; 9 | import androidx.core.app.ActivityCompat; 10 | import androidx.core.content.ContextCompat; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Map; 14 | 15 | import io.flutter.plugin.common.MethodCall; 16 | import io.flutter.plugin.common.MethodChannel; 17 | import io.flutter.plugin.common.PluginRegistry; 18 | 19 | public class AsrPlugin implements MethodChannel.MethodCallHandler { 20 | 21 | private static final String TAG = "AsrPlugin"; 22 | private final Activity activity; 23 | private ResultStateful resultStateful; 24 | private AsrManager asrManager; 25 | 26 | public static void registerWith(PluginRegistry.Registrar registrar) { 27 | //消息地址 28 | MethodChannel channel = new MethodChannel(registrar.messenger(), "asr_plugin"); 29 | AsrPlugin instance = new AsrPlugin(registrar); 30 | channel.setMethodCallHandler(instance); 31 | } 32 | 33 | public AsrPlugin(PluginRegistry.Registrar registrar) { 34 | this.activity = registrar.activity(); 35 | } 36 | 37 | @Override 38 | public void onMethodCall(MethodCall call, MethodChannel.Result result) { 39 | initPermission(); 40 | switch (call.method) { 41 | case "start": 42 | resultStateful = ResultStateful.of(result); 43 | start(call,resultStateful); 44 | break; 45 | case "stop": 46 | stop(call,result); 47 | break; 48 | case "cancel": 49 | cancel(call,result); 50 | break; 51 | default: 52 | break; 53 | } 54 | } 55 | 56 | private void start(MethodCall call, ResultStateful result) { 57 | if(activity == null){ 58 | Log.e(TAG,"Ignored start,current activity is null"); 59 | result.error("Ignored start,current activity is null",null,null); 60 | return; 61 | } 62 | if(getAsrManager() != null){ 63 | getAsrManager().start(call.arguments instanceof Map ? (Map)call.arguments:null); 64 | }else{ 65 | Log.e(TAG,"Ignored start,current getAsrManager is null"); 66 | result.error("Ignored start,current getAsrManager is null",null,null); 67 | } 68 | 69 | } 70 | 71 | private void stop(MethodCall call,MethodChannel.Result result){ 72 | if(asrManager != null){ 73 | asrManager.stop(); 74 | } 75 | } 76 | 77 | private void cancel(MethodCall call,MethodChannel.Result result){ 78 | if(asrManager != null){ 79 | asrManager.cancel(); 80 | } 81 | } 82 | 83 | @Nullable 84 | private AsrManager getAsrManager(){ 85 | if(asrManager == null){ 86 | if(activity != null && !activity.isFinishing()){ 87 | asrManager = new AsrManager(activity,onAsrListener); 88 | } 89 | } 90 | return asrManager; 91 | } 92 | 93 | /** 94 | * android 6.0 以上需要动态申请权限 95 | */ 96 | private void initPermission() { 97 | String permissions[] = {Manifest.permission.RECORD_AUDIO, 98 | Manifest.permission.ACCESS_NETWORK_STATE, 99 | Manifest.permission.INTERNET, 100 | Manifest.permission.WRITE_EXTERNAL_STORAGE 101 | }; 102 | 103 | ArrayList toApplyList = new ArrayList(); 104 | 105 | for (String perm :permissions){ 106 | if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(activity, perm)) { 107 | toApplyList.add(perm); 108 | //进入到这里代表没有权限. 109 | 110 | } 111 | } 112 | String tmpList[] = new String[toApplyList.size()]; 113 | if (!toApplyList.isEmpty()){ 114 | ActivityCompat.requestPermissions(activity, toApplyList.toArray(tmpList), 123); 115 | } 116 | 117 | } 118 | private OnAsrListener onAsrListener = new OnAsrListener() { 119 | @Override 120 | public void onAsrReady() { 121 | 122 | } 123 | 124 | @Override 125 | public void onAsrBegin() { 126 | 127 | } 128 | 129 | @Override 130 | public void onAsrEnd() { 131 | 132 | } 133 | 134 | @Override 135 | public void onAsrPartialResult(String[] results, RecogResult recogResult) { 136 | 137 | } 138 | 139 | @Override 140 | public void onAsrOnlineNluResult(String nluResult) { 141 | 142 | } 143 | 144 | @Override 145 | public void onAsrFinalResult(String[] results, RecogResult recogResult) { 146 | if(resultStateful != null){ 147 | resultStateful.success(results[0]); 148 | } 149 | } 150 | 151 | @Override 152 | public void onAsrFinish(RecogResult recogResult) { 153 | 154 | } 155 | 156 | @Override 157 | public void onAsrFinishError(int errorCode, int subErrorCode, String descMessage, RecogResult recogResult) { 158 | if(resultStateful != null){ 159 | resultStateful.error(descMessage,null,null); 160 | } 161 | } 162 | 163 | @Override 164 | public void onAsrLongFinish() { 165 | 166 | } 167 | 168 | @Override 169 | public void onAsrVolume(int volumePercent, int volume) { 170 | 171 | } 172 | 173 | @Override 174 | public void onAsrAudio(byte[] data, int offset, int length) { 175 | 176 | } 177 | 178 | @Override 179 | public void onAsrExit() { 180 | 181 | } 182 | 183 | @Override 184 | public void onOfflineLoaded() { 185 | 186 | } 187 | 188 | @Override 189 | public void onOfflineUnLoaded() { 190 | 191 | } 192 | }; 193 | } 194 | -------------------------------------------------------------------------------- /android/asr_plugin/src/main/java/com/rjx/asr_plugin/OnAsrListener.java: -------------------------------------------------------------------------------- 1 | package com.rjx.asr_plugin; 2 | 3 | 4 | /** 5 | * 与SDK中回调参数的对应关系定义在RecogEventAdapter类中 6 | */ 7 | 8 | public interface OnAsrListener { 9 | 10 | /** 11 | * CALLBACK_EVENT_ASR_READY 12 | * ASR_START 输入事件调用后,引擎准备完毕 13 | */ 14 | void onAsrReady(); 15 | 16 | /** 17 | * CALLBACK_EVENT_ASR_BEGIN 18 | * onAsrReady后检查到用户开始说话 19 | */ 20 | void onAsrBegin(); 21 | 22 | /** 23 | * CALLBACK_EVENT_ASR_END 24 | * 检查到用户开始说话停止,或者ASR_STOP 输入事件调用后, 25 | */ 26 | void onAsrEnd(); 27 | 28 | /** 29 | * CALLBACK_EVENT_ASR_PARTIAL resultType=partial_result 30 | * onAsrBegin 后 随着用户的说话,返回的临时结果 31 | * 32 | * @param results 可能返回多个结果,请取第一个结果 33 | * @param recogResult 完整的结果 34 | */ 35 | void onAsrPartialResult(String[] results, RecogResult recogResult); 36 | 37 | /** 38 | * 语音的在线语义结果 39 | * 40 | * CALLBACK_EVENT_ASR_PARTIAL resultType=nlu_result 41 | * @param nluResult 42 | */ 43 | void onAsrOnlineNluResult(String nluResult); 44 | 45 | /** 46 | * 不开启长语音仅回调一次,长语音的每一句话都会回调一次 47 | * CALLBACK_EVENT_ASR_PARTIAL resultType=final_result 48 | * 最终的识别结果 49 | * 50 | * @param results 可能返回多个结果,请取第一个结果 51 | * @param recogResult 完整的结果 52 | */ 53 | void onAsrFinalResult(String[] results, RecogResult recogResult); 54 | 55 | /** 56 | * CALLBACK_EVENT_ASR_FINISH 57 | * @param recogResult 结束识别 58 | */ 59 | void onAsrFinish(RecogResult recogResult); 60 | 61 | /** 62 | * CALLBACK_EVENT_ASR_FINISH error!=0 63 | * 64 | * @param errorCode 65 | * @param subErrorCode 66 | * @param descMessage 67 | * @param recogResult 68 | */ 69 | void onAsrFinishError(int errorCode, int subErrorCode, String descMessage, 70 | RecogResult recogResult); 71 | 72 | /** 73 | * 长语音识别结束 74 | */ 75 | void onAsrLongFinish(); 76 | 77 | /** 78 | * CALLBACK_EVENT_ASR_VOLUME 79 | * 音量回调 80 | * 81 | * @param volumePercent 音量的相对值,百分比,0-100 82 | * @param volume 音量绝对值 83 | */ 84 | void onAsrVolume(int volumePercent, int volume); 85 | 86 | /** 87 | * CALLBACK_EVENT_ASR_AUDIO 88 | * @param data pcm格式,16bits 16000采样率 89 | * 90 | * @param offset 91 | * @param length 92 | */ 93 | void onAsrAudio(byte[] data, int offset, int length); 94 | 95 | /** 96 | * CALLBACK_EVENT_ASR_EXIT 97 | * 引擎完成整个识别,空闲中 98 | */ 99 | void onAsrExit(); 100 | 101 | /** 102 | * CALLBACK_EVENT_ASR_LOADED 103 | * 离线命令词资源加载成功 104 | */ 105 | void onOfflineLoaded(); 106 | 107 | /** 108 | * CALLBACK_EVENT_ASR_UNLOADED 109 | * 离线命令词资源释放成功 110 | */ 111 | void onOfflineUnLoaded(); 112 | } 113 | -------------------------------------------------------------------------------- /android/asr_plugin/src/main/java/com/rjx/asr_plugin/RecogEventAdapter.java: -------------------------------------------------------------------------------- 1 | package com.rjx.asr_plugin; 2 | 3 | import android.util.Log; 4 | import com.baidu.speech.EventListener; 5 | import com.baidu.speech.asr.SpeechConstant; 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | /** 10 | * Created by fujiayi on 2017/6/14. 11 | */ 12 | 13 | public class RecogEventAdapter implements EventListener { 14 | 15 | private OnAsrListener listener; 16 | 17 | private static final String TAG = "RecogEventAdapter"; 18 | 19 | public RecogEventAdapter(OnAsrListener listener) { 20 | this.listener = listener; 21 | } 22 | 23 | // 基于DEMO集成3.1 开始回调事件 24 | @Override 25 | public void onEvent(String name, String params, byte[] data, int offset, int length) { 26 | String currentJson = params; 27 | String logMessage = "name:" + name + "; params:" + params; 28 | 29 | // logcat 中 搜索RecogEventAdapter,即可以看见下面一行的日志 30 | Log.i(TAG, logMessage); 31 | if (false) { // 可以调试,不需要后续逻辑 32 | return; 33 | } 34 | if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_LOADED)) { 35 | listener.onOfflineLoaded(); 36 | } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_UNLOADED)) { 37 | listener.onOfflineUnLoaded(); 38 | } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_READY)) { 39 | // 引擎准备就绪,可以开始说话 40 | listener.onAsrReady(); 41 | } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_BEGIN)) { 42 | // 检测到用户的已经开始说话 43 | listener.onAsrBegin(); 44 | 45 | } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_END)) { 46 | // 检测到用户的已经停止说话 47 | listener.onAsrEnd(); 48 | 49 | } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL)) { 50 | RecogResult recogResult = RecogResult.parseJson(params); 51 | // 识别结果 52 | String[] results = recogResult.getResultsRecognition(); 53 | if (recogResult.isFinalResult()) { 54 | // 最终识别结果,长语音每一句话会回调一次 55 | listener.onAsrFinalResult(results, recogResult); 56 | } else if (recogResult.isPartialResult()) { 57 | // 临时识别结果 58 | listener.onAsrPartialResult(results, recogResult); 59 | } else if (recogResult.isNluResult()) { 60 | // 语义理解结果 61 | listener.onAsrOnlineNluResult(new String(data, offset, length)); 62 | } 63 | 64 | } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_FINISH)) { 65 | // 识别结束 66 | RecogResult recogResult = RecogResult.parseJson(params); 67 | if (recogResult.hasError()) { 68 | int errorCode = recogResult.getError(); 69 | int subErrorCode = recogResult.getSubError(); 70 | Log.i(TAG, "asr error:" + params); 71 | listener.onAsrFinishError(errorCode, subErrorCode, recogResult.getDesc(), recogResult); 72 | } else { 73 | listener.onAsrFinish(recogResult); 74 | } 75 | } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_LONG_SPEECH)) { // 长语音 76 | listener.onAsrLongFinish(); // 长语音 77 | } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_EXIT)) { 78 | listener.onAsrExit(); 79 | } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_VOLUME)) { 80 | // Logger.info(TAG, "asr volume event:" + params); 81 | Volume vol = parseVolumeJson(params); 82 | listener.onAsrVolume(vol.volumePercent, vol.volume); 83 | } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_AUDIO)) { 84 | if (data.length != length) { 85 | Log.e(TAG, "internal error: asr.audio callback data length is not equal to length param"); 86 | } 87 | listener.onAsrAudio(data, offset, length); 88 | } 89 | } 90 | 91 | private Volume parseVolumeJson(String jsonStr) { 92 | Volume vol = new Volume(); 93 | vol.origalJson = jsonStr; 94 | try { 95 | JSONObject json = new JSONObject(jsonStr); 96 | vol.volumePercent = json.getInt("volume-percent"); 97 | vol.volume = json.getInt("volume"); 98 | } catch (JSONException e) { 99 | e.printStackTrace(); 100 | } 101 | return vol; 102 | } 103 | 104 | private class Volume { 105 | private int volumePercent = -1; 106 | private int volume = -1; 107 | private String origalJson; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /android/asr_plugin/src/main/java/com/rjx/asr_plugin/RecogResult.java: -------------------------------------------------------------------------------- 1 | package com.rjx.asr_plugin; 2 | 3 | import org.json.JSONArray; 4 | import org.json.JSONException; 5 | import org.json.JSONObject; 6 | 7 | /** 8 | * Created by fujiayi on 2017/6/24. 9 | */ 10 | public class RecogResult { 11 | private static final int ERROR_NONE = 0; 12 | 13 | private String origalJson; 14 | private String[] resultsRecognition; 15 | private String origalResult; 16 | private String sn; // 日志id, 请求有问题请提问带上sn 17 | private String desc; 18 | private String resultType; 19 | private int error = -1; 20 | private int subError = -1; 21 | 22 | public static RecogResult parseJson(String jsonStr) { 23 | RecogResult result = new RecogResult(); 24 | result.setOrigalJson(jsonStr); 25 | try { 26 | JSONObject json = new JSONObject(jsonStr); 27 | int error = json.optInt("error"); 28 | int subError = json.optInt("sub_error"); 29 | result.setError(error); 30 | result.setDesc(json.optString("desc")); 31 | result.setResultType(json.optString("result_type")); 32 | result.setSubError(subError); 33 | if (error == ERROR_NONE) { 34 | result.setOrigalResult(json.getString("origin_result")); 35 | JSONArray arr = json.optJSONArray("results_recognition"); 36 | if (arr != null) { 37 | int size = arr.length(); 38 | String[] recogs = new String[size]; 39 | for (int i = 0; i < size; i++) { 40 | recogs[i] = arr.getString(i); 41 | } 42 | result.setResultsRecognition(recogs); 43 | } 44 | 45 | 46 | } 47 | } catch (JSONException e) { 48 | e.printStackTrace(); 49 | } 50 | 51 | return result; 52 | } 53 | 54 | public boolean hasError() { 55 | return error != ERROR_NONE; 56 | } 57 | 58 | public boolean isFinalResult() { 59 | return "final_result".equals(resultType); 60 | } 61 | 62 | 63 | public boolean isPartialResult() { 64 | return "partial_result".equals(resultType); 65 | } 66 | 67 | public boolean isNluResult() { 68 | return "nlu_result".equals(resultType); 69 | } 70 | 71 | public String getOrigalJson() { 72 | return origalJson; 73 | } 74 | 75 | public void setOrigalJson(String origalJson) { 76 | this.origalJson = origalJson; 77 | } 78 | 79 | public String[] getResultsRecognition() { 80 | return resultsRecognition; 81 | } 82 | 83 | public void setResultsRecognition(String[] resultsRecognition) { 84 | this.resultsRecognition = resultsRecognition; 85 | } 86 | 87 | public String getSn() { 88 | return sn; 89 | } 90 | 91 | public void setSn(String sn) { 92 | this.sn = sn; 93 | } 94 | 95 | public int getError() { 96 | return error; 97 | } 98 | 99 | public void setError(int error) { 100 | this.error = error; 101 | } 102 | 103 | public String getDesc() { 104 | return desc; 105 | } 106 | 107 | public void setDesc(String desc) { 108 | this.desc = desc; 109 | } 110 | 111 | public String getOrigalResult() { 112 | return origalResult; 113 | } 114 | 115 | public void setOrigalResult(String origalResult) { 116 | this.origalResult = origalResult; 117 | } 118 | 119 | public String getResultType() { 120 | return resultType; 121 | } 122 | 123 | public void setResultType(String resultType) { 124 | this.resultType = resultType; 125 | } 126 | 127 | public int getSubError() { 128 | return subError; 129 | } 130 | 131 | public void setSubError(int subError) { 132 | this.subError = subError; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /android/asr_plugin/src/main/java/com/rjx/asr_plugin/ResultStateful.java: -------------------------------------------------------------------------------- 1 | package com.rjx.asr_plugin; 2 | 3 | import android.util.Log; 4 | 5 | import io.flutter.plugin.common.MethodChannel; 6 | 7 | public class ResultStateful implements MethodChannel.Result { 8 | private static final String TAG = "ResultStateful"; 9 | private MethodChannel.Result result; 10 | private boolean called; 11 | 12 | public ResultStateful(MethodChannel.Result result) { 13 | this.result = result; 14 | } 15 | 16 | public static ResultStateful of(MethodChannel.Result result) { 17 | return new ResultStateful(result); 18 | } 19 | 20 | @Override 21 | public void success(Object o) { 22 | if (called) { 23 | Log.e(TAG, "Has called before"); 24 | return; 25 | } 26 | called = true; 27 | this.result.success(o); 28 | 29 | 30 | } 31 | 32 | @Override 33 | public void error(String errorCode, String errorMessage, Object errorDetails) { 34 | if (called) { 35 | Log.e(TAG, "Has called error before"); 36 | return; 37 | } 38 | called = true; 39 | this.result.error(errorCode,errorMessage,errorDetails); 40 | } 41 | 42 | @Override 43 | public void notImplemented() { 44 | if (called) { 45 | Log.e(TAG, "Has called not Implemented"); 46 | return; 47 | } 48 | called = true; 49 | this.result.notImplemented(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /android/asr_plugin/src/main/jniLibs/arm64-v8a/libBaiduSpeechSDK.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/android/asr_plugin/src/main/jniLibs/arm64-v8a/libBaiduSpeechSDK.so -------------------------------------------------------------------------------- /android/asr_plugin/src/main/jniLibs/arm64-v8a/libvad.dnn.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/android/asr_plugin/src/main/jniLibs/arm64-v8a/libvad.dnn.so -------------------------------------------------------------------------------- /android/asr_plugin/src/main/jniLibs/x86/libBaiduSpeechSDK.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/android/asr_plugin/src/main/jniLibs/x86/libBaiduSpeechSDK.so -------------------------------------------------------------------------------- /android/asr_plugin/src/main/jniLibs/x86/libvad.dnn.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/android/asr_plugin/src/main/jniLibs/x86/libvad.dnn.so -------------------------------------------------------------------------------- /android/asr_plugin/src/main/jniLibs/x86_64/libBaiduSpeechSDK.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/android/asr_plugin/src/main/jniLibs/x86_64/libBaiduSpeechSDK.so -------------------------------------------------------------------------------- /android/asr_plugin/src/main/jniLibs/x86_64/libvad.dnn.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/android/asr_plugin/src/main/jniLibs/x86_64/libvad.dnn.so -------------------------------------------------------------------------------- /android/asr_plugin/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | asr_plugin 3 | 4 | -------------------------------------------------------------------------------- /android/asr_plugin/src/test/java/com/rjx/asr_plugin/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.rjx.asr_plugin; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'} 5 | google() 6 | jcenter() 7 | } 8 | 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.5.0' 11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | maven{ url 'http://maven.aliyun.com/nexus/content/groups/public/'} 18 | google() 19 | jcenter() 20 | } 21 | } 22 | 23 | rootProject.buildDir = '../build' 24 | subprojects { 25 | project.buildDir = "${rootProject.buildDir}/${project.name}" 26 | } 27 | subprojects { 28 | project.evaluationDependsOn(':app') 29 | } 30 | 31 | task clean(type: Delete) { 32 | delete rootProject.buildDir 33 | } 34 | -------------------------------------------------------------------------------- /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/key.properties: -------------------------------------------------------------------------------- 1 | storePassword=xiecheng 2 | keyPassword=xiecheng 3 | keyAlias=xiecheng 4 | storeFile=../xiecheng.jks -------------------------------------------------------------------------------- /android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -keep class com.baidu.sppch.**{*;} -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':asr_plugin' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /android/xiecheng.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/android/xiecheng.jks -------------------------------------------------------------------------------- /app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/app-release.apk -------------------------------------------------------------------------------- /images/type_channelgroup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/images/type_channelgroup.png -------------------------------------------------------------------------------- /images/type_channellgs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/images/type_channellgs.png -------------------------------------------------------------------------------- /images/type_channelplane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/images/type_channelplane.png -------------------------------------------------------------------------------- /images/type_channeltrain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/images/type_channeltrain.png -------------------------------------------------------------------------------- /images/type_cruise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/images/type_cruise.png -------------------------------------------------------------------------------- /images/type_district.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/images/type_district.png -------------------------------------------------------------------------------- /images/type_food.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/images/type_food.png -------------------------------------------------------------------------------- /images/type_hotel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/images/type_hotel.png -------------------------------------------------------------------------------- /images/type_huodong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/images/type_huodong.png -------------------------------------------------------------------------------- /images/type_shop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/images/type_shop.png -------------------------------------------------------------------------------- /images/type_sight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/images/type_sight.png -------------------------------------------------------------------------------- /images/type_ticket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/images/type_ticket.png -------------------------------------------------------------------------------- /images/type_travelgroup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/images/type_travelgroup.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/Generated.xcconfig 20 | Flutter/app.flx 21 | Flutter/app.zip 22 | Flutter/flutter_assets/ 23 | Flutter/flutter_export_environment.sh 24 | ServiceDefinitions.json 25 | Runner/GeneratedPluginRegistrant.* 26 | 27 | # Exceptions to above rules. 28 | !default.mode1v3 29 | !default.mode2v3 30 | !default.pbxuser 31 | !default.perspectivev3 32 | -------------------------------------------------------------------------------- /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 "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 13 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 14 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 15 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 16 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 17 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 18 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 19 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXCopyFilesBuildPhase section */ 23 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 24 | isa = PBXCopyFilesBuildPhase; 25 | buildActionMask = 2147483647; 26 | dstPath = ""; 27 | dstSubfolderSpec = 10; 28 | files = ( 29 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 30 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, 31 | ); 32 | name = "Embed Frameworks"; 33 | runOnlyForDeploymentPostprocessing = 0; 34 | }; 35 | /* End PBXCopyFilesBuildPhase section */ 36 | 37 | /* Begin PBXFileReference section */ 38 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 39 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 40 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 41 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 42 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 43 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 44 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 45 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 46 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 47 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 48 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 49 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 50 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 51 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 52 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 61 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | /* End PBXFrameworksBuildPhase section */ 66 | 67 | /* Begin PBXGroup section */ 68 | 9740EEB11CF90186004384FC /* Flutter */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 3B80C3931E831B6300D905FE /* App.framework */, 72 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 73 | 9740EEBA1CF902C7004384FC /* Flutter.framework */, 74 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 75 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 76 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 77 | ); 78 | name = Flutter; 79 | sourceTree = ""; 80 | }; 81 | 97C146E51CF9000F007C117D = { 82 | isa = PBXGroup; 83 | children = ( 84 | 9740EEB11CF90186004384FC /* Flutter */, 85 | 97C146F01CF9000F007C117D /* Runner */, 86 | 97C146EF1CF9000F007C117D /* Products */, 87 | ); 88 | sourceTree = ""; 89 | }; 90 | 97C146EF1CF9000F007C117D /* Products */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 97C146EE1CF9000F007C117D /* Runner.app */, 94 | ); 95 | name = Products; 96 | sourceTree = ""; 97 | }; 98 | 97C146F01CF9000F007C117D /* Runner */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 102 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 103 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 104 | 97C147021CF9000F007C117D /* Info.plist */, 105 | 97C146F11CF9000F007C117D /* Supporting Files */, 106 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 107 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 108 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 109 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 110 | ); 111 | path = Runner; 112 | sourceTree = ""; 113 | }; 114 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | ); 118 | name = "Supporting Files"; 119 | sourceTree = ""; 120 | }; 121 | /* End PBXGroup section */ 122 | 123 | /* Begin PBXNativeTarget section */ 124 | 97C146ED1CF9000F007C117D /* Runner */ = { 125 | isa = PBXNativeTarget; 126 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 127 | buildPhases = ( 128 | 9740EEB61CF901F6004384FC /* Run Script */, 129 | 97C146EA1CF9000F007C117D /* Sources */, 130 | 97C146EB1CF9000F007C117D /* Frameworks */, 131 | 97C146EC1CF9000F007C117D /* Resources */, 132 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 133 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 134 | ); 135 | buildRules = ( 136 | ); 137 | dependencies = ( 138 | ); 139 | name = Runner; 140 | productName = Runner; 141 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 142 | productType = "com.apple.product-type.application"; 143 | }; 144 | /* End PBXNativeTarget section */ 145 | 146 | /* Begin PBXProject section */ 147 | 97C146E61CF9000F007C117D /* Project object */ = { 148 | isa = PBXProject; 149 | attributes = { 150 | LastUpgradeCheck = 1020; 151 | ORGANIZATIONNAME = "The Chromium Authors"; 152 | TargetAttributes = { 153 | 97C146ED1CF9000F007C117D = { 154 | CreatedOnToolsVersion = 7.3.1; 155 | LastSwiftMigration = 0910; 156 | }; 157 | }; 158 | }; 159 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 160 | compatibilityVersion = "Xcode 3.2"; 161 | developmentRegion = en; 162 | hasScannedForEncodings = 0; 163 | knownRegions = ( 164 | en, 165 | Base, 166 | ); 167 | mainGroup = 97C146E51CF9000F007C117D; 168 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 169 | projectDirPath = ""; 170 | projectRoot = ""; 171 | targets = ( 172 | 97C146ED1CF9000F007C117D /* Runner */, 173 | ); 174 | }; 175 | /* End PBXProject section */ 176 | 177 | /* Begin PBXResourcesBuildPhase section */ 178 | 97C146EC1CF9000F007C117D /* Resources */ = { 179 | isa = PBXResourcesBuildPhase; 180 | buildActionMask = 2147483647; 181 | files = ( 182 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 183 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 184 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 185 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 186 | ); 187 | runOnlyForDeploymentPostprocessing = 0; 188 | }; 189 | /* End PBXResourcesBuildPhase section */ 190 | 191 | /* Begin PBXShellScriptBuildPhase section */ 192 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 193 | isa = PBXShellScriptBuildPhase; 194 | buildActionMask = 2147483647; 195 | files = ( 196 | ); 197 | inputPaths = ( 198 | ); 199 | name = "Thin Binary"; 200 | outputPaths = ( 201 | ); 202 | runOnlyForDeploymentPostprocessing = 0; 203 | shellPath = /bin/sh; 204 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; 205 | }; 206 | 9740EEB61CF901F6004384FC /* Run Script */ = { 207 | isa = PBXShellScriptBuildPhase; 208 | buildActionMask = 2147483647; 209 | files = ( 210 | ); 211 | inputPaths = ( 212 | ); 213 | name = "Run Script"; 214 | outputPaths = ( 215 | ); 216 | runOnlyForDeploymentPostprocessing = 0; 217 | shellPath = /bin/sh; 218 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 219 | }; 220 | /* End PBXShellScriptBuildPhase section */ 221 | 222 | /* Begin PBXSourcesBuildPhase section */ 223 | 97C146EA1CF9000F007C117D /* Sources */ = { 224 | isa = PBXSourcesBuildPhase; 225 | buildActionMask = 2147483647; 226 | files = ( 227 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 228 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | }; 232 | /* End PBXSourcesBuildPhase section */ 233 | 234 | /* Begin PBXVariantGroup section */ 235 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 236 | isa = PBXVariantGroup; 237 | children = ( 238 | 97C146FB1CF9000F007C117D /* Base */, 239 | ); 240 | name = Main.storyboard; 241 | sourceTree = ""; 242 | }; 243 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 244 | isa = PBXVariantGroup; 245 | children = ( 246 | 97C147001CF9000F007C117D /* Base */, 247 | ); 248 | name = LaunchScreen.storyboard; 249 | sourceTree = ""; 250 | }; 251 | /* End PBXVariantGroup section */ 252 | 253 | /* Begin XCBuildConfiguration section */ 254 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 255 | isa = XCBuildConfiguration; 256 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 257 | buildSettings = { 258 | ALWAYS_SEARCH_USER_PATHS = NO; 259 | CLANG_ANALYZER_NONNULL = YES; 260 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 261 | CLANG_CXX_LIBRARY = "libc++"; 262 | CLANG_ENABLE_MODULES = YES; 263 | CLANG_ENABLE_OBJC_ARC = YES; 264 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 265 | CLANG_WARN_BOOL_CONVERSION = YES; 266 | CLANG_WARN_COMMA = YES; 267 | CLANG_WARN_CONSTANT_CONVERSION = YES; 268 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 269 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 270 | CLANG_WARN_EMPTY_BODY = YES; 271 | CLANG_WARN_ENUM_CONVERSION = YES; 272 | CLANG_WARN_INFINITE_RECURSION = YES; 273 | CLANG_WARN_INT_CONVERSION = YES; 274 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 275 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 276 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 277 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 278 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 279 | CLANG_WARN_STRICT_PROTOTYPES = YES; 280 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 281 | CLANG_WARN_UNREACHABLE_CODE = YES; 282 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 283 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 284 | COPY_PHASE_STRIP = NO; 285 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 286 | ENABLE_NS_ASSERTIONS = NO; 287 | ENABLE_STRICT_OBJC_MSGSEND = YES; 288 | GCC_C_LANGUAGE_STANDARD = gnu99; 289 | GCC_NO_COMMON_BLOCKS = YES; 290 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 291 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 292 | GCC_WARN_UNDECLARED_SELECTOR = YES; 293 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 294 | GCC_WARN_UNUSED_FUNCTION = YES; 295 | GCC_WARN_UNUSED_VARIABLE = YES; 296 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 297 | MTL_ENABLE_DEBUG_INFO = NO; 298 | SDKROOT = iphoneos; 299 | SUPPORTED_PLATFORMS = iphoneos; 300 | TARGETED_DEVICE_FAMILY = "1,2"; 301 | VALIDATE_PRODUCT = YES; 302 | }; 303 | name = Profile; 304 | }; 305 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 306 | isa = XCBuildConfiguration; 307 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 308 | buildSettings = { 309 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 310 | CLANG_ENABLE_MODULES = YES; 311 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 312 | ENABLE_BITCODE = NO; 313 | FRAMEWORK_SEARCH_PATHS = ( 314 | "$(inherited)", 315 | "$(PROJECT_DIR)/Flutter", 316 | ); 317 | INFOPLIST_FILE = Runner/Info.plist; 318 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 319 | LIBRARY_SEARCH_PATHS = ( 320 | "$(inherited)", 321 | "$(PROJECT_DIR)/Flutter", 322 | ); 323 | PRODUCT_BUNDLE_IDENTIFIER = com.rjx.xiechengwangApp; 324 | PRODUCT_NAME = "$(TARGET_NAME)"; 325 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 326 | SWIFT_VERSION = 4.0; 327 | VERSIONING_SYSTEM = "apple-generic"; 328 | }; 329 | name = Profile; 330 | }; 331 | 97C147031CF9000F007C117D /* Debug */ = { 332 | isa = XCBuildConfiguration; 333 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 334 | buildSettings = { 335 | ALWAYS_SEARCH_USER_PATHS = NO; 336 | CLANG_ANALYZER_NONNULL = YES; 337 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 338 | CLANG_CXX_LIBRARY = "libc++"; 339 | CLANG_ENABLE_MODULES = YES; 340 | CLANG_ENABLE_OBJC_ARC = YES; 341 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 342 | CLANG_WARN_BOOL_CONVERSION = YES; 343 | CLANG_WARN_COMMA = YES; 344 | CLANG_WARN_CONSTANT_CONVERSION = YES; 345 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 346 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 347 | CLANG_WARN_EMPTY_BODY = YES; 348 | CLANG_WARN_ENUM_CONVERSION = YES; 349 | CLANG_WARN_INFINITE_RECURSION = YES; 350 | CLANG_WARN_INT_CONVERSION = YES; 351 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 352 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 353 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 354 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 355 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 356 | CLANG_WARN_STRICT_PROTOTYPES = YES; 357 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 358 | CLANG_WARN_UNREACHABLE_CODE = YES; 359 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 360 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 361 | COPY_PHASE_STRIP = NO; 362 | DEBUG_INFORMATION_FORMAT = dwarf; 363 | ENABLE_STRICT_OBJC_MSGSEND = YES; 364 | ENABLE_TESTABILITY = YES; 365 | GCC_C_LANGUAGE_STANDARD = gnu99; 366 | GCC_DYNAMIC_NO_PIC = NO; 367 | GCC_NO_COMMON_BLOCKS = YES; 368 | GCC_OPTIMIZATION_LEVEL = 0; 369 | GCC_PREPROCESSOR_DEFINITIONS = ( 370 | "DEBUG=1", 371 | "$(inherited)", 372 | ); 373 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 374 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 375 | GCC_WARN_UNDECLARED_SELECTOR = YES; 376 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 377 | GCC_WARN_UNUSED_FUNCTION = YES; 378 | GCC_WARN_UNUSED_VARIABLE = YES; 379 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 380 | MTL_ENABLE_DEBUG_INFO = YES; 381 | ONLY_ACTIVE_ARCH = YES; 382 | SDKROOT = iphoneos; 383 | TARGETED_DEVICE_FAMILY = "1,2"; 384 | }; 385 | name = Debug; 386 | }; 387 | 97C147041CF9000F007C117D /* Release */ = { 388 | isa = XCBuildConfiguration; 389 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 390 | buildSettings = { 391 | ALWAYS_SEARCH_USER_PATHS = NO; 392 | CLANG_ANALYZER_NONNULL = YES; 393 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 394 | CLANG_CXX_LIBRARY = "libc++"; 395 | CLANG_ENABLE_MODULES = YES; 396 | CLANG_ENABLE_OBJC_ARC = YES; 397 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 398 | CLANG_WARN_BOOL_CONVERSION = YES; 399 | CLANG_WARN_COMMA = YES; 400 | CLANG_WARN_CONSTANT_CONVERSION = YES; 401 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 402 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 403 | CLANG_WARN_EMPTY_BODY = YES; 404 | CLANG_WARN_ENUM_CONVERSION = YES; 405 | CLANG_WARN_INFINITE_RECURSION = YES; 406 | CLANG_WARN_INT_CONVERSION = YES; 407 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 408 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 409 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 410 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 411 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 412 | CLANG_WARN_STRICT_PROTOTYPES = YES; 413 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 414 | CLANG_WARN_UNREACHABLE_CODE = YES; 415 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 416 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 417 | COPY_PHASE_STRIP = NO; 418 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 419 | ENABLE_NS_ASSERTIONS = NO; 420 | ENABLE_STRICT_OBJC_MSGSEND = YES; 421 | GCC_C_LANGUAGE_STANDARD = gnu99; 422 | GCC_NO_COMMON_BLOCKS = YES; 423 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 424 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 425 | GCC_WARN_UNDECLARED_SELECTOR = YES; 426 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 427 | GCC_WARN_UNUSED_FUNCTION = YES; 428 | GCC_WARN_UNUSED_VARIABLE = YES; 429 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 430 | MTL_ENABLE_DEBUG_INFO = NO; 431 | SDKROOT = iphoneos; 432 | SUPPORTED_PLATFORMS = iphoneos; 433 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 434 | TARGETED_DEVICE_FAMILY = "1,2"; 435 | VALIDATE_PRODUCT = YES; 436 | }; 437 | name = Release; 438 | }; 439 | 97C147061CF9000F007C117D /* Debug */ = { 440 | isa = XCBuildConfiguration; 441 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 442 | buildSettings = { 443 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 444 | CLANG_ENABLE_MODULES = YES; 445 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 446 | ENABLE_BITCODE = NO; 447 | FRAMEWORK_SEARCH_PATHS = ( 448 | "$(inherited)", 449 | "$(PROJECT_DIR)/Flutter", 450 | ); 451 | INFOPLIST_FILE = Runner/Info.plist; 452 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 453 | LIBRARY_SEARCH_PATHS = ( 454 | "$(inherited)", 455 | "$(PROJECT_DIR)/Flutter", 456 | ); 457 | PRODUCT_BUNDLE_IDENTIFIER = com.rjx.xiechengwangApp; 458 | PRODUCT_NAME = "$(TARGET_NAME)"; 459 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 460 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 461 | SWIFT_VERSION = 4.0; 462 | VERSIONING_SYSTEM = "apple-generic"; 463 | }; 464 | name = Debug; 465 | }; 466 | 97C147071CF9000F007C117D /* Release */ = { 467 | isa = XCBuildConfiguration; 468 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 469 | buildSettings = { 470 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 471 | CLANG_ENABLE_MODULES = YES; 472 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 473 | ENABLE_BITCODE = NO; 474 | FRAMEWORK_SEARCH_PATHS = ( 475 | "$(inherited)", 476 | "$(PROJECT_DIR)/Flutter", 477 | ); 478 | INFOPLIST_FILE = Runner/Info.plist; 479 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 480 | LIBRARY_SEARCH_PATHS = ( 481 | "$(inherited)", 482 | "$(PROJECT_DIR)/Flutter", 483 | ); 484 | PRODUCT_BUNDLE_IDENTIFIER = com.rjx.xiechengwangApp; 485 | PRODUCT_NAME = "$(TARGET_NAME)"; 486 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 487 | SWIFT_VERSION = 4.0; 488 | VERSIONING_SYSTEM = "apple-generic"; 489 | }; 490 | name = Release; 491 | }; 492 | /* End XCBuildConfiguration section */ 493 | 494 | /* Begin XCConfigurationList section */ 495 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 496 | isa = XCConfigurationList; 497 | buildConfigurations = ( 498 | 97C147031CF9000F007C117D /* Debug */, 499 | 97C147041CF9000F007C117D /* Release */, 500 | 249021D3217E4FDB00AE95B9 /* Profile */, 501 | ); 502 | defaultConfigurationIsVisible = 0; 503 | defaultConfigurationName = Release; 504 | }; 505 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 506 | isa = XCConfigurationList; 507 | buildConfigurations = ( 508 | 97C147061CF9000F007C117D /* Debug */, 509 | 97C147071CF9000F007C117D /* Release */, 510 | 249021D4217E4FDB00AE95B9 /* Profile */, 511 | ); 512 | defaultConfigurationIsVisible = 0; 513 | defaultConfigurationName = Release; 514 | }; 515 | /* End XCConfigurationList section */ 516 | 517 | }; 518 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 519 | } 520 | -------------------------------------------------------------------------------- /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 | 8 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | xiechengwang_app 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 | 45 | 46 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /lib/dao/home_dao.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'package:http/http.dart' as http; 4 | import 'package:xiechengwang_app/model/home_model.dart'; 5 | 6 | const HOME_URL = "https://www.devio.org/io/flutter_app/json/home_page.json"; 7 | 8 | class HomeDao{ 9 | static Future fetch() async{ 10 | final response = await http.get(HOME_URL); 11 | if(response.statusCode == 200){ 12 | //fix中文乱码 13 | Utf8Decoder utf8decoder = Utf8Decoder(); 14 | var result = json.decode(utf8decoder.convert(response.bodyBytes)); 15 | return HomeModel.fromJson(result); 16 | }else{ 17 | throw Exception("Failed to load home_page.json"); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /lib/dao/search_dao.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'package:http/http.dart' as http; 4 | import 'package:xiechengwang_app/model/search_model.dart'; 5 | 6 | 7 | class SearchDao{ 8 | static Future fetch(String url,String keyword) async{ 9 | final response = await http.get(url); 10 | if(response.statusCode == 200){ 11 | //fix中文乱码 12 | Utf8Decoder utf8decoder = Utf8Decoder(); 13 | var result = json.decode(utf8decoder.convert(response.bodyBytes)); 14 | SearchModel model = SearchModel.fromJson(result); 15 | model.keyword = keyword; 16 | return model; 17 | }else{ 18 | throw Exception("Failed to Search"); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /lib/dao/travel_dao.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'package:http/http.dart' as http; 3 | import 'package:xiechengwang_app/model/travel_model.dart'; 4 | 5 | // ignore: non_constant_identifier_names 6 | var Params = { 7 | "districtId": -1, 8 | "groupChannelCode": "tourphoto_global1", 9 | "type": null, 10 | "lat": -180, 11 | "lon": -180, 12 | "locatedDistrictId": 2, 13 | "pagePara": { 14 | "pageIndex": 1, 15 | "pageSize": 10, 16 | "sortType": 9, 17 | "sortDirection": 0 18 | }, 19 | "imageCutType": 1, 20 | "head": { 21 | "cid": "09031014111431397988", 22 | "ctok": "", 23 | "cver": "1.0", 24 | "lang": "01", 25 | "sid": "8888", 26 | "syscode": "09", 27 | "auth": null, 28 | "extension": [ 29 | { 30 | "name": "protocal", 31 | "value": "https" 32 | } 33 | ] 34 | }, 35 | "contentType": "json" 36 | 37 | }; 38 | class TravelDao{ 39 | static Future fetch(String url,String groupChannelCode,int pageIndex,int pageSize) async{ 40 | Map paramsMap = Params['pagePara']; 41 | paramsMap['pageIndex'] = pageIndex; 42 | paramsMap['pageSize'] = pageSize; 43 | Params['groupChannelCode'] = groupChannelCode; 44 | 45 | final response = await http.post(url,body: jsonEncode(Params)); 46 | if(response.statusCode == 200){ 47 | Utf8Decoder utf8decoder = Utf8Decoder();//fix中文乱码 48 | var result = json.decode(utf8decoder.convert(response.bodyBytes)); 49 | return TravelModel.fromJson(result); 50 | }else{ 51 | throw Exception("Failed to load travel"); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /lib/dao/travel_tab_dao.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:xiechengwang_app/model/travel_tab_model.dart'; 4 | import 'package:http/http.dart' as http; 5 | 6 | const TAB_URL = "https://www.devio.org/io/flutter_app/json/travel_page.json"; 7 | 8 | class TravelTabDao{ 9 | static Future fetch() async{ 10 | final response = await http.get(TAB_URL); 11 | if(response.statusCode == 200){ 12 | Utf8Decoder utf8decoder = Utf8Decoder();//fix中文乱码 13 | var result = json.decode(utf8decoder.convert(response.bodyBytes)); 14 | return TravelTabModel.fromJson(result); 15 | }else{ 16 | throw Exception("Failed to load travel_page.json"); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:xiechengwang_app/navigator/tab_navigator.dart'; 3 | 4 | 5 | void main() => runApp(MyApp()); 6 | 7 | class MyApp extends StatelessWidget { 8 | // This widget is the root of your application. 9 | @override 10 | Widget build(BuildContext context) { 11 | return MaterialApp( 12 | title: '携程旅行', 13 | theme: ThemeData( 14 | // This is the theme of your application. 15 | // 16 | // Try running your application with "flutter run". You'll see the 17 | // application has a blue toolbar. Then, without quitting the app, try 18 | // changing the primarySwatch below to Colors.green and then invoke 19 | // "hot reload" (press "r" in the console where you ran "flutter run", 20 | // or simply save your changes to "hot reload" in a Flutter IDE). 21 | // Notice that the counter didn't reset back to zero; the application 22 | // is not restarted. 23 | primarySwatch: Colors.blue, 24 | ), 25 | home: TabNavigator() 26 | ); 27 | } 28 | } 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /lib/model/common_model.dart: -------------------------------------------------------------------------------- 1 | 2 | class CommonModel{ 3 | final String icon; 4 | final String title; 5 | final String url; 6 | final String statusBarColor; 7 | final bool hideAppBar; 8 | 9 | CommonModel({this.icon, this.title, this.url, this.statusBarColor, 10 | this.hideAppBar}); 11 | 12 | factory CommonModel.fromJson(Map json) { 13 | return CommonModel(icon: json["icon"], 14 | title: json["title"], 15 | url: json["url"], 16 | statusBarColor: json["statusBarColor"], 17 | hideAppBar: json["hideAppBar"],); 18 | } 19 | 20 | Map toJson() { 21 | return { 22 | "icon": this.icon, 23 | "title": this.title, 24 | "url": this.url, 25 | "statusBarColor": this.statusBarColor, 26 | "hideAppBar": this.hideAppBar, 27 | }; 28 | } 29 | 30 | 31 | } -------------------------------------------------------------------------------- /lib/model/config_mdoel.dart: -------------------------------------------------------------------------------- 1 | 2 | class ConfigModel{ 3 | 4 | final String searchUrl; 5 | 6 | ConfigModel(this.searchUrl); 7 | 8 | factory ConfigModel.fromJson(Map json){ 9 | return ConfigModel(json['searchUrl']); 10 | } 11 | 12 | Map toJson() { 13 | return {"searchUrl": this.searchUrl,}; 14 | } 15 | 16 | 17 | } -------------------------------------------------------------------------------- /lib/model/grid_nav_model.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:xiechengwang_app/model/common_model.dart'; 3 | 4 | class GridNavModel{ 5 | 6 | final GridNavItem hotel; 7 | final GridNavItem flight; 8 | final GridNavItem travel; 9 | 10 | GridNavModel({this.hotel, this.flight, this.travel}); 11 | 12 | factory GridNavModel.fromJson(Map json) { 13 | return GridNavModel(hotel: GridNavItem.fromJson(json["hotel"]), 14 | flight: GridNavItem.fromJson(json["flight"]), 15 | travel: GridNavItem.fromJson(json["travel"]),); 16 | } 17 | 18 | Map toJson() { 19 | return {"hotel": this.hotel, "flight": this.flight, "travel": this.travel,}; 20 | } 21 | 22 | } 23 | 24 | class GridNavItem{ 25 | final String startColor; 26 | final String endColor; 27 | final CommonModel mainItem; 28 | final CommonModel item1; 29 | final CommonModel item2; 30 | final CommonModel item3; 31 | final CommonModel item4; 32 | 33 | GridNavItem({this.startColor, this.endColor, this.mainItem, this.item1, 34 | this.item2, this.item3, this.item4}); 35 | 36 | factory GridNavItem.fromJson(Map json) { 37 | return GridNavItem(startColor: json["startColor"], 38 | endColor: json["endColor"], 39 | mainItem: CommonModel.fromJson(json["mainItem"]), 40 | item1: CommonModel.fromJson(json["item1"]), 41 | item2: CommonModel.fromJson(json["item2"]), 42 | item3: CommonModel.fromJson(json["item3"]), 43 | item4: CommonModel.fromJson(json["item4"]),); 44 | } 45 | 46 | Map toJson() { 47 | return { 48 | "startColor": this.startColor, 49 | "endColor": this.endColor, 50 | "mainItem": this.mainItem, 51 | "item1": this.item1, 52 | "item2": this.item2, 53 | "item3": this.item3, 54 | "item4": this.item4, 55 | }; 56 | } 57 | 58 | 59 | } -------------------------------------------------------------------------------- /lib/model/home_model.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:xiechengwang_app/model/common_model.dart'; 3 | import 'package:xiechengwang_app/model/config_mdoel.dart'; 4 | import 'package:xiechengwang_app/model/sales_box_model.dart'; 5 | 6 | import 'grid_nav_model.dart'; 7 | 8 | class HomeModel{ 9 | 10 | final ConfigModel config; 11 | final List bannerList; 12 | final List localNavList; 13 | final GridNavModel gridNav; 14 | final List subNavList; 15 | final SalesBoxModel salesBox; 16 | 17 | HomeModel({this.config, this.bannerList, this.localNavList, this.gridNav, 18 | this.subNavList, this.salesBox}); 19 | 20 | factory HomeModel.fromJson(Map json) { 21 | return HomeModel(config: ConfigModel.fromJson(json["config"]), 22 | bannerList: List.of(json["bannerList"]).map(( 23 | i) => CommonModel.fromJson(i)).toList(), 24 | localNavList: List.of(json["localNavList"]).map(( 25 | i) => CommonModel.fromJson(i)).toList(), 26 | gridNav: GridNavModel.fromJson(json["gridNav"]), 27 | subNavList: List.of(json["subNavList"]).map(( 28 | i) => CommonModel.fromJson(i)).toList(), 29 | salesBox: SalesBoxModel.fromJson(json["salesBox"]),); 30 | } 31 | 32 | Map toJson() { 33 | return { 34 | "config": this.config, 35 | "bannerList": jsonEncode(this.bannerList), 36 | "localNavList": jsonEncode(this.localNavList), 37 | "gridNav": this.gridNav, 38 | "subNavList": jsonEncode(this.subNavList), 39 | "salesBox": this.salesBox, 40 | }; 41 | } 42 | static List jsonEncode(List list){ 43 | List jsonList = List(); 44 | list.map((item)=> 45 | jsonList.add(item.toJson()) 46 | ).toList(); 47 | return jsonList; 48 | } 49 | 50 | 51 | } -------------------------------------------------------------------------------- /lib/model/sales_box_model.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:xiechengwang_app/model/common_model.dart'; 3 | 4 | class SalesBoxModel{ 5 | final String icon; 6 | final String moreUrl; 7 | final CommonModel bigCard1; 8 | final CommonModel bigCard2; 9 | final CommonModel smallCard1; 10 | final CommonModel smallCard2; 11 | final CommonModel smallCard3; 12 | final CommonModel smallCard4; 13 | 14 | SalesBoxModel({this.icon, this.moreUrl, this.bigCard1, this.bigCard2, 15 | this.smallCard1, this.smallCard2,this.smallCard3,this.smallCard4}); 16 | 17 | factory SalesBoxModel.fromJson(Map json) { 18 | return SalesBoxModel(icon: json["icon"], 19 | moreUrl: json["moreUrl"], 20 | bigCard1: CommonModel.fromJson(json["bigCard1"]), 21 | bigCard2: CommonModel.fromJson(json["bigCard2"]), 22 | smallCard1: CommonModel.fromJson(json["smallCard1"]), 23 | smallCard2: CommonModel.fromJson(json["smallCard2"]), 24 | smallCard3: CommonModel.fromJson(json["smallCard3"]), 25 | smallCard4: CommonModel.fromJson(json["smallCard4"]), 26 | ); 27 | } 28 | 29 | Map toJson() { 30 | return { 31 | "icon": this.icon, 32 | "moreUrl": this.moreUrl, 33 | "bigCard1": this.bigCard1, 34 | "bigCard2": this.bigCard2, 35 | "smallCard1": this.smallCard1, 36 | "smallCard2": this.smallCard2, 37 | "smallCard3": this.smallCard3, 38 | "smallCard4": this.smallCard4, 39 | }; 40 | } 41 | 42 | 43 | } -------------------------------------------------------------------------------- /lib/model/search_model.dart: -------------------------------------------------------------------------------- 1 | //搜索模型 2 | class SearchModel { 3 | String keyword; 4 | final List data; 5 | 6 | SearchModel({this.data,this.keyword}); 7 | 8 | factory SearchModel.fromJson(Map json) { 9 | return SearchModel( 10 | data: List.of(json["data"]) 11 | .map((i) => SearchItem.fromJson(i)) 12 | .toList(), 13 | ); 14 | } 15 | } 16 | 17 | class SearchItem { 18 | final String word; //xxx酒店 19 | final String type; //hotel 20 | final String price; //实时计价 21 | final String zonename; //虹桥 22 | final String districtname; //上海 23 | final String url; 24 | final String star; 25 | 26 | SearchItem( 27 | {this.word, 28 | this.type, 29 | this.price, 30 | this.zonename, 31 | this.districtname, 32 | this.url,this.star}); 33 | 34 | factory SearchItem.fromJson(Map json) { 35 | return SearchItem( 36 | word: json["word"], 37 | type: json["type"], 38 | price: json["price"], 39 | zonename: json["zonename"], 40 | districtname: json["districtname"], 41 | url: json["url"], 42 | star: json["star"] 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/model/travel_model.dart: -------------------------------------------------------------------------------- 1 | ///旅拍类别模型 2 | class TravelModel { 3 | int totalCount; 4 | List resultList; 5 | 6 | TravelModel({this.totalCount, this.resultList}); 7 | 8 | factory TravelModel.fromJson(Map json) { 9 | return TravelModel(totalCount: json["totalCount"], 10 | resultList: List.of(json["resultList"]).map(( 11 | i) => TravelItem.fromJson(i)).toList(),); 12 | } 13 | 14 | } 15 | class TravelItem{ 16 | 17 | int type; 18 | Article article; 19 | 20 | TravelItem({this.type, this.article}); 21 | 22 | factory TravelItem.fromJson(Map json) { 23 | return TravelItem(type: json["type"], 24 | article: Article.fromJson(json["article"]),); 25 | } 26 | 27 | } 28 | class Article{ 29 | int articleId; 30 | String articleType; 31 | int productType; 32 | int sourceType; 33 | String articleTitle; 34 | Author author; 35 | List images; 36 | bool hasVideo; 37 | int readCount; 38 | int likeCount; 39 | int commentCount; 40 | List urls; 41 | List topics; 42 | List pois; 43 | String publishTime; 44 | String publishTimeDisplay; 45 | String shootTime; 46 | String shootTimeDisplay; 47 | int level; 48 | String distanceText; 49 | bool isLike; 50 | int collectCount; 51 | int articleStatus; 52 | String poiName; 53 | 54 | Article({this.articleId, this.articleType, this.productType, this.sourceType, 55 | this.articleTitle, this.author, this.images, this.hasVideo, 56 | this.readCount, this.likeCount, this.commentCount, this.urls, 57 | this.topics, this.pois, this.publishTime, this.publishTimeDisplay, 58 | this.shootTime, this.shootTimeDisplay, this.level, this.distanceText, 59 | this.isLike, this.collectCount, this.articleStatus, this.poiName}); 60 | 61 | factory Article.fromJson(Map json) { 62 | return Article(articleId: json["articleId"], 63 | articleType: json["articleType"], 64 | productType: json["productType"], 65 | sourceType: json["sourceType"], 66 | articleTitle: json["articleTitle"], 67 | author: Author.fromJson(json["author"]), 68 | images: List.of(json["images"]).map(( 69 | i) => Images.fromJson(i)).toList(), 70 | hasVideo: json["hasVideo"], 71 | readCount: json["readCount"], 72 | likeCount: json["likeCount"], 73 | commentCount: json["commentCount"], 74 | urls: List.of(json["urls"]).map(( 75 | i) => Urls.fromJson(i)).toList(), 76 | topics: List.of(json["topics"]).map(( 77 | i) => Topics.fromJson(i)).toList(), 78 | pois: List.of(json["pois"]).map(( 79 | i) => Pois.fromJson(i)).toList(), 80 | publishTime: json["publishTime"], 81 | publishTimeDisplay: json["publishTimeDisplay"], 82 | shootTime: json["shootTime"], 83 | shootTimeDisplay: json["shootTimeDisplay"], 84 | level: json["level"], 85 | distanceText: json["distanceText"], 86 | isLike: json["isLike"], 87 | collectCount: json["collectCount"], 88 | articleStatus: json["articleStatus"], 89 | poiName: json["poiName"],); 90 | } 91 | 92 | 93 | } 94 | class Author{ 95 | int authorId; 96 | String nickName; 97 | String clientAuth; 98 | String jumpUrl; 99 | CoverImage coverImage; 100 | int identityType; 101 | String tag; 102 | 103 | Author({this.authorId, this.nickName, this.clientAuth, this.jumpUrl, 104 | this.coverImage, this.identityType, this.tag}); 105 | 106 | factory Author.fromJson(Map json) { 107 | return Author(authorId: json["authorId"], 108 | nickName: json["nickName"], 109 | clientAuth: json["clientAuth"], 110 | jumpUrl: json["jumpUrl"], 111 | coverImage: CoverImage.fromJson(json["coverImage"]), 112 | identityType: json["identityType"], 113 | tag: json["tag"],); 114 | } 115 | 116 | 117 | } 118 | 119 | class Images{ 120 | int imageId; 121 | String dynamicUrl; 122 | String originalUrl; 123 | double width; 124 | double height; 125 | int mediaType; 126 | bool isWaterMarked; 127 | 128 | Images({this.imageId, this.dynamicUrl, this.originalUrl, this.width, 129 | this.height, this.mediaType, this.isWaterMarked}); 130 | 131 | factory Images.fromJson(Map json) { 132 | return Images(imageId:json["imageId"], 133 | dynamicUrl: json["dynamicUrl"], 134 | originalUrl: json["originalUrl"], 135 | width: json["width"], 136 | height: json["height"], 137 | mediaType: json["mediaType"], 138 | isWaterMarked: json["isWaterMarked"],); 139 | } 140 | 141 | } 142 | class CoverImage{ 143 | String dynamicUrl; 144 | String originalUrl; 145 | 146 | CoverImage({this.dynamicUrl, this.originalUrl}); 147 | 148 | factory CoverImage.fromJson(Map json) { 149 | return CoverImage( 150 | dynamicUrl: json["dynamicUrl"], originalUrl: json["originalUrl"],); 151 | } 152 | 153 | } 154 | 155 | class Urls{ 156 | String version; 157 | String appUrl; 158 | String h5Url; 159 | String wxUrl; 160 | 161 | Urls({this.version, this.appUrl, this.h5Url, this.wxUrl}); 162 | 163 | factory Urls.fromJson(Map json) { 164 | return Urls(version: json["version"], 165 | appUrl: json["appUrl"], 166 | h5Url: json["h5Url"], 167 | wxUrl: json["wxUrl"],); 168 | } 169 | 170 | } 171 | 172 | class Topics{ 173 | int topicId; 174 | String topicName; 175 | int level; 176 | 177 | Topics({this.topicId, this.topicName, this.level}); 178 | 179 | factory Topics.fromJson(Map json) { 180 | return Topics(topicId: json["topicId"], 181 | topicName: json["topicName"], 182 | level: json["level"],); 183 | } 184 | 185 | } 186 | class Pois{ 187 | int poiType; 188 | int poiId; 189 | String poiName; 190 | int businessId; 191 | int districtId; 192 | PoiExt poiExt; 193 | int source; 194 | int isMain; 195 | bool isInChina; 196 | String countryName; 197 | 198 | Pois({this.poiType, this.poiId, this.poiName, this.businessId, this.districtId, 199 | this.poiExt, this.source, this.isMain, this.isInChina, this.countryName}); 200 | 201 | factory Pois.fromJson(Map json) { 202 | return Pois(poiType: json["poiType"], 203 | poiId: json["poiId"], 204 | poiName: json["poiName"], 205 | businessId: json["businessId"], 206 | districtId:json["districtId"], 207 | poiExt: PoiExt.fromJson(json["poiExt"]), 208 | source: json["source"], 209 | isMain: json["isMain"], 210 | isInChina: json["isInChina"], 211 | countryName: json["countryName"],); 212 | } 213 | 214 | 215 | 216 | } 217 | 218 | class PoiExt{ 219 | String h5Url; 220 | String appUrl; 221 | 222 | PoiExt({this.h5Url, this.appUrl}); 223 | 224 | factory PoiExt.fromJson(Map json) { 225 | return PoiExt(h5Url: json["h5Url"], appUrl: json["appUrl"],); 226 | } 227 | 228 | 229 | 230 | } -------------------------------------------------------------------------------- /lib/model/travel_tab_model.dart: -------------------------------------------------------------------------------- 1 | class TravelTabModel { 2 | String url; 3 | List tabs; 4 | 5 | TravelTabModel({this.url, this.tabs}); 6 | 7 | 8 | factory TravelTabModel.fromJson(Map json) { 9 | return TravelTabModel(url: json["url"], 10 | tabs: List.of(json["tabs"]).map(( 11 | i) => TravelTab.fromJson(i)).toList(),); 12 | } 13 | 14 | Map toJson() { 15 | final Map data = new Map(); 16 | data['url'] = this.url; 17 | if (this.tabs != null) { 18 | data['tabs'] = this.tabs.map((v) => v.toJson()).toList(); 19 | } 20 | return data; 21 | } 22 | } 23 | 24 | class TravelTab { 25 | String labelName; 26 | String groupChannelCode; 27 | 28 | TravelTab({this.labelName, this.groupChannelCode}); 29 | 30 | TravelTab.fromJson(Map json) { 31 | labelName = json['labelName']; 32 | groupChannelCode = json['groupChannelCode']; 33 | } 34 | 35 | Map toJson() { 36 | final Map data = new Map(); 37 | data['labelName'] = this.labelName; 38 | data['groupChannelCode'] = this.groupChannelCode; 39 | return data; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/navigator/tab_navigator.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:xiechengwang_app/pages/home_page.dart'; 3 | import 'package:xiechengwang_app/pages/my_page.dart'; 4 | import 'package:xiechengwang_app/pages/search_page.dart'; 5 | import 'package:xiechengwang_app/pages/travel_page.dart'; 6 | 7 | class TabNavigator extends StatefulWidget { 8 | @override 9 | State createState() { 10 | // TODO: implement createState 11 | return _TabNavigatorState(); 12 | } 13 | } 14 | 15 | class _TabNavigatorState extends State { 16 | final _defaultColor = Colors.grey; 17 | final _activeColor = Colors.blue; 18 | int _currentIndex = 0; 19 | 20 | final PageController _controller = PageController( 21 | initialPage: 0, 22 | ); 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | // TODO: implement build 27 | return Scaffold( 28 | body: PageView( 29 | controller: _controller, 30 | children: [ 31 | HomePage(), 32 | SearchPage(hideLeft: true,), 33 | TravelPage(), 34 | MyPage()], 35 | //禁止滑动 36 | physics: NeverScrollableScrollPhysics(), 37 | ), 38 | bottomNavigationBar: BottomNavigationBar( 39 | currentIndex: _currentIndex, 40 | onTap: (index) { 41 | _controller.jumpToPage(index); 42 | setState(() { 43 | _currentIndex = index; 44 | }); 45 | }, 46 | type: BottomNavigationBarType.fixed, 47 | items: [ 48 | _bottomNavigationBar("首页",Icons.home,0), 49 | _bottomNavigationBar("搜索",Icons.search,1), 50 | _bottomNavigationBar("旅拍",Icons.camera_alt,2), 51 | _bottomNavigationBar("我的",Icons.account_circle,3), 52 | ], 53 | ), 54 | ); 55 | } 56 | 57 | _bottomNavigationBar(String title, IconData icon, int index) { 58 | return BottomNavigationBarItem( 59 | icon: Icon(icon, color: _defaultColor,), 60 | activeIcon: Icon(icon, color: _activeColor,), 61 | title:Text(title,style: TextStyle(color: _currentIndex !=index ? _defaultColor:_activeColor),), 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/pages/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_splash_screen/flutter_splash_screen.dart'; 5 | import 'package:flutter_swiper/flutter_swiper.dart'; 6 | import 'package:xiechengwang_app/dao/home_dao.dart'; 7 | import 'package:xiechengwang_app/model/common_model.dart'; 8 | import 'package:xiechengwang_app/model/grid_nav_model.dart'; 9 | import 'package:xiechengwang_app/model/sales_box_model.dart'; 10 | import 'package:xiechengwang_app/pages/search_page.dart'; 11 | import 'package:xiechengwang_app/pages/speak_page.dart'; 12 | import 'package:xiechengwang_app/util/navigator_util.dart'; 13 | import 'package:xiechengwang_app/widget/grid_nav.dart'; 14 | import 'package:xiechengwang_app/widget/loading_container.dart'; 15 | import 'package:xiechengwang_app/widget/local_nav.dart'; 16 | import 'package:xiechengwang_app/widget/sales_box.dart'; 17 | import 'package:xiechengwang_app/widget/search_bar.dart'; 18 | import 'package:xiechengwang_app/widget/sub_nav.dart'; 19 | import 'package:xiechengwang_app/widget/webview.dart'; 20 | 21 | const APPBAR_SCROLL_OFFSET = 100; 22 | const String SEARCH_BAR_DEFAULT_TEXT = "网红打卡地 景点 酒店 美食"; 23 | 24 | class HomePage extends StatefulWidget{ 25 | @override 26 | _HomePageState createState() { 27 | // TODO: implement createState 28 | return _HomePageState(); 29 | } 30 | } 31 | 32 | class _HomePageState extends State with AutomaticKeepAliveClientMixin{ 33 | List localNavList = []; 34 | GridNavModel gridNavModel; 35 | List subNavList = []; 36 | List bannerList = []; 37 | SalesBoxModel salesBoxModel; 38 | bool _loading = true; 39 | 40 | String resultString = ""; 41 | double appBarAlpha = 0; 42 | 43 | @override 44 | void initState() { 45 | super.initState(); 46 | _handleRefresh(); 47 | //关闭启动屏 48 | Future.delayed(Duration(milliseconds: 600),(){ 49 | FlutterSplashScreen.hide(); 50 | }); 51 | 52 | } 53 | 54 | Future _handleRefresh() { 55 | return HomeDao.fetch().then((result) { 56 | setState(() { 57 | bannerList = result.bannerList; 58 | localNavList = result.localNavList; 59 | gridNavModel = result.gridNav; 60 | subNavList = result.subNavList; 61 | salesBoxModel = result.salesBox; 62 | _loading = false; 63 | }); 64 | }).catchError((error) { 65 | print(error.toString()); 66 | _loading = false; 67 | }); 68 | 69 | // HomeModel model = await HomeDao.fetch(); 70 | // setState(() { 71 | // resultString = json.encode(model); 72 | // }); 73 | } 74 | 75 | _onScroll(offset) { 76 | double alpha = offset / APPBAR_SCROLL_OFFSET; 77 | 78 | if (alpha < 0) { 79 | alpha = 0; 80 | } else if (alpha > 1) { 81 | alpha = 1; 82 | } 83 | 84 | setState(() { 85 | appBarAlpha = alpha; 86 | }); 87 | } 88 | 89 | @override 90 | Widget build(BuildContext context) { 91 | // TODO: implement build 92 | return Scaffold( 93 | backgroundColor: Color(0xfff2f2), 94 | body: LoadingContainer( 95 | isLoading: _loading, 96 | child: Stack( 97 | children: [ 98 | MediaQuery.removePadding( 99 | context: context, 100 | removeTop: true, 101 | child: RefreshIndicator( 102 | //下拉刷新 103 | onRefresh: _handleRefresh, 104 | child: NotificationListener( 105 | onNotification: (scrollNotification) { 106 | if (scrollNotification is ScrollUpdateNotification && 107 | scrollNotification.depth == 0) { 108 | _onScroll(scrollNotification.metrics.pixels); 109 | } 110 | return; 111 | }, 112 | child: _listView, 113 | ), 114 | )), 115 | _appBar, 116 | ], 117 | ), 118 | )); 119 | } 120 | 121 | Widget get _listView { 122 | return ListView( 123 | children: [ 124 | _bannerView, 125 | Padding( 126 | padding: EdgeInsets.fromLTRB(7, 4, 7, 4), 127 | child: LocalNav( 128 | localNavList: localNavList, 129 | ), 130 | ), 131 | Padding( 132 | padding: EdgeInsets.fromLTRB(7, 0, 7, 4), 133 | child: GridNav(gridNavModel: gridNavModel), 134 | ), 135 | Padding( 136 | padding: EdgeInsets.fromLTRB(7, 0, 7, 4), 137 | child: SubNav(subNavList: subNavList), 138 | ), 139 | Padding( 140 | padding: EdgeInsets.fromLTRB(7, 0, 7, 4), 141 | child: SalesBox(salesBox: salesBoxModel), 142 | ), 143 | ], 144 | ); 145 | } 146 | 147 | Widget get _appBar { 148 | // return Opacity( 149 | // opacity: appBarAlpha, 150 | // child: Container( 151 | // height: 70, 152 | // decoration: BoxDecoration(color: Colors.white), 153 | // alignment: Alignment.center, 154 | // padding: EdgeInsets.only(top: MediaQueryData.fromWindow(window).padding.top), 155 | // child: Text("首页") 156 | // , 157 | // ), 158 | // ); 159 | return Column( 160 | children: [ 161 | Container( 162 | decoration: BoxDecoration( 163 | gradient: LinearGradient( 164 | //AppBar渐变遮罩背景 165 | colors: [Color(0x66000000), Colors.transparent], 166 | begin: Alignment.topCenter, 167 | end: Alignment.bottomCenter, 168 | ), 169 | ), 170 | child: Container( 171 | padding: EdgeInsets.fromLTRB(0, MediaQueryData.fromWindow(window).padding.top, 0, 0), 172 | height: 80, 173 | alignment: Alignment.center, 174 | decoration: BoxDecoration( 175 | color: 176 | Color.fromARGB((appBarAlpha * 255).toInt(), 255, 255, 255)), 177 | child: SearchBar( 178 | searchBarType: appBarAlpha > 0.3 179 | ? SearchBarType.homeLight 180 | : SearchBarType.home, 181 | inputBoxClick: _jumpToSearch, 182 | speakClick: _jumpToSpeak, 183 | defaultText: SEARCH_BAR_DEFAULT_TEXT, 184 | leftButtonClick: () {}, 185 | ), 186 | ), 187 | ), 188 | //阴影设置 189 | Container( 190 | height: appBarAlpha > 0.3 ? 0.5 : 0, 191 | decoration: BoxDecoration( 192 | boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 0.5)]), 193 | ), 194 | ], 195 | ); 196 | } 197 | 198 | Widget get _bannerView { 199 | return Container( 200 | height: 160, 201 | child: Swiper( 202 | itemCount: bannerList.length, 203 | autoplay: true, 204 | itemBuilder: (BuildContext context, int index) { 205 | return GestureDetector( 206 | onTap: () { 207 | CommonModel model = bannerList[index]; 208 | NavigatorUtil.push(context, WebView( 209 | url: model.url, 210 | title: model.title, 211 | hideAppBar: model.hideAppBar)); 212 | }, 213 | child: Image.network( 214 | bannerList[index].icon, 215 | fit: BoxFit.fill, 216 | ), 217 | ); 218 | }, 219 | pagination: SwiperPagination(), //指示器 220 | ), 221 | ); 222 | } 223 | 224 | _jumpToSearch() { 225 | NavigatorUtil.push(context, SearchPage(hint: SEARCH_BAR_DEFAULT_TEXT,)); 226 | } 227 | 228 | 229 | _jumpToSpeak() { 230 | NavigatorUtil.push(context, SpeakPage()); 231 | } 232 | 233 | @override 234 | bool get wantKeepAlive => true; 235 | } 236 | -------------------------------------------------------------------------------- /lib/pages/my_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:xiechengwang_app/widget/webview.dart'; 3 | 4 | class MyPage extends StatefulWidget { 5 | @override 6 | _MyPageState createState() { 7 | // TODO: implement createState 8 | return _MyPageState(); 9 | } 10 | } 11 | 12 | class _MyPageState extends State { 13 | @override 14 | Widget build(BuildContext context) { 15 | // TODO: implement build 16 | return Scaffold( 17 | body: WebView( 18 | url: "https://m.ctrip.com/webapp/myctrip/", 19 | hideAppBar: true, 20 | backForbid: true, 21 | statusBarColor: "4c5bca", 22 | ), 23 | ); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /lib/pages/search_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:xiechengwang_app/dao/search_dao.dart'; 5 | import 'package:xiechengwang_app/model/search_model.dart'; 6 | import 'package:xiechengwang_app/pages/speak_page.dart'; 7 | import 'package:xiechengwang_app/util/navigator_util.dart'; 8 | import 'package:xiechengwang_app/widget/search_bar.dart'; 9 | import 'package:xiechengwang_app/widget/webview.dart'; 10 | 11 | //首页接口的searchUrl返回了该接口 12 | const URL = 13 | "https://m.ctrip.com/restapi/h5api/searchapp/search?source=mobileweb&action=autocomplete&contentType=json&keyword="; 14 | 15 | const TYPES = [ 16 | 'channelgroup', 17 | 'channellgs', 18 | 'channelplane', 19 | 'channeltrain', 20 | 'cruise', 21 | 'district', 22 | 'food', 23 | 'hotel', 24 | 'huodong', 25 | 'shop', 26 | 'sight', 27 | 'ticket', 28 | 'travelgroup' 29 | ]; 30 | 31 | class SearchPage extends StatefulWidget { 32 | final bool hideLeft; 33 | final String searchUrl; 34 | final String keyword; 35 | final String hint; 36 | 37 | const SearchPage( 38 | {Key key, this.hideLeft, this.searchUrl = URL, this.keyword, this.hint}) 39 | : super(key: key); 40 | 41 | @override 42 | _SearchPage createState() { 43 | return _SearchPage(); 44 | } 45 | } 46 | 47 | class _SearchPage extends State { 48 | String keyword; 49 | SearchModel searchModel; 50 | 51 | @override 52 | void initState() { 53 | if(widget.keyword != null){ 54 | _onTextChange(widget.keyword); 55 | } 56 | super.initState(); 57 | } 58 | @override 59 | Widget build(BuildContext context) { 60 | // TODO: implement build 61 | return Scaffold( 62 | body: Column( 63 | children: [ 64 | _appBar(), 65 | MediaQuery.removePadding( 66 | context: context, 67 | removeTop: true, 68 | child: Expanded( 69 | flex: 1, 70 | //顶部会有空白 用MediaQuery.removePadding 71 | child: ListView.builder( 72 | itemCount: searchModel?.data?.length ?? 0, 73 | itemBuilder: (BuildContext context, int position) { 74 | return _item(position); 75 | }), 76 | ), 77 | ), 78 | ], 79 | ), 80 | ); 81 | } 82 | 83 | _onTextChange(String text) { 84 | //发起请求 85 | keyword = text; 86 | if (text.length == 0) { 87 | setState(() { 88 | searchModel = null; 89 | }); 90 | return; 91 | } 92 | String url = widget.searchUrl + keyword; 93 | SearchDao.fetch(url, keyword).then((model) { 94 | if (model.keyword == keyword) { 95 | //解决快速请求展示结果不是最后一次搜索结果的问题 96 | setState(() { 97 | searchModel = model; 98 | }); 99 | } 100 | }).catchError((e) { 101 | print(e.toString()); 102 | }); 103 | } 104 | 105 | _item(int position) { 106 | if (searchModel == null || searchModel.data == null) { 107 | return null; 108 | } 109 | SearchItem item = searchModel.data[position]; 110 | return GestureDetector( 111 | onTap: () { 112 | NavigatorUtil.push(context, WebView(url: item.url, title: "详情")); 113 | }, 114 | child: Container( 115 | padding: EdgeInsets.all(10), 116 | decoration: BoxDecoration( 117 | border: Border(bottom: BorderSide(width: 0.3, color: Colors.grey))), 118 | child: Row( 119 | children: [ 120 | Container( 121 | margin: EdgeInsets.all(1), 122 | child: Image( 123 | height: 26, 124 | width: 26, 125 | image: AssetImage(_typeImage(item.type)), 126 | ), 127 | ), 128 | Column( 129 | children: [ 130 | Container(width: 300, child: _title(item)), 131 | Container( 132 | margin: EdgeInsets.only(top: 5), 133 | width: 300, 134 | child: _subTitle(item), 135 | ) 136 | ], 137 | ) 138 | ], 139 | ), 140 | ), 141 | ); 142 | } 143 | 144 | _appBar() { 145 | return Column( 146 | children: [ 147 | Container( 148 | decoration: BoxDecoration( 149 | gradient: LinearGradient( 150 | colors: [Color(0x66000000), Colors.transparent], 151 | begin: Alignment.topCenter, 152 | end: Alignment.bottomCenter, 153 | ), 154 | ), 155 | child: Container( 156 | padding: EdgeInsets.only( 157 | top: MediaQueryData.fromWindow(window).padding.top), 158 | height: 80, 159 | alignment: Alignment.center, 160 | decoration: BoxDecoration(color: Colors.white), 161 | child: SearchBar( 162 | hideLeft: widget.hideLeft, 163 | defaultText: widget.keyword, 164 | hint: widget.hint, 165 | speakClick: _jumpToSpeak, 166 | leftButtonClick: () { 167 | Navigator.pop(context); 168 | }, 169 | onChanged: _onTextChange, 170 | ), 171 | ), 172 | ), 173 | ], 174 | ); 175 | } 176 | 177 | _typeImage(String type) { 178 | if (type == null) { 179 | return "images/type_travelgroup.png"; 180 | } 181 | String path = 'travelgroup'; 182 | for (final val in TYPES) { 183 | if (type.contains(val)) { 184 | path = val; 185 | break; 186 | } 187 | } 188 | return "images/type_$path.png"; 189 | } 190 | 191 | _title(SearchItem item) { 192 | if (item == null) { 193 | return null; 194 | } 195 | List spans = []; 196 | spans.addAll(_keywordTextSpans(item.word, searchModel.keyword)); 197 | spans.add(TextSpan( 198 | text: ' ' + (item.districtname ?? "") + " " + (item.zonename ?? ""), 199 | style: TextStyle(fontSize: 16, color: Colors.grey))); 200 | return RichText(text: TextSpan(children: spans)); 201 | } 202 | 203 | _subTitle(SearchItem item) { 204 | return RichText( 205 | text: TextSpan(children: [ 206 | TextSpan( 207 | text: item.price ?? "", 208 | style: TextStyle(fontSize: 16, color: Colors.orange)), 209 | TextSpan( 210 | text: ' ' + (item.star ?? ''), 211 | style: TextStyle(fontSize: 12, color: Colors.grey)) 212 | ]), 213 | ); 214 | } 215 | 216 | _keywordTextSpans(String word, String keyword) { 217 | List spans = []; 218 | if (word == null || word.length == 0) { 219 | return spans; 220 | } 221 | 222 | List arr = word.split(keyword); 223 | TextStyle normalStyle = TextStyle(fontSize: 16, color: Colors.black87); 224 | TextStyle keywordStyle = TextStyle(fontSize: 16, color: Colors.orange); 225 | // wordwoc split w [,ord,c] 226 | for (int i = 0; i < arr.length; i++) { 227 | if ((i + 1) % 2 == 0) { 228 | spans.add(TextSpan(text: keyword, style: keywordStyle)); 229 | } 230 | String val = arr[i]; 231 | if (val != null && val.length > 0) { 232 | spans.add(TextSpan(text: val, style: normalStyle)); 233 | } 234 | } 235 | return spans; 236 | } 237 | 238 | _jumpToSpeak() { 239 | NavigatorUtil.push(context, SpeakPage()); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /lib/pages/speak_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:xiechengwang_app/pages/search_page.dart'; 4 | import 'package:xiechengwang_app/plugin/asr_manager.dart'; 5 | import 'package:xiechengwang_app/util/navigator_util.dart'; 6 | 7 | class SpeakPage extends StatefulWidget { 8 | @override 9 | _SpeakPageState createState() { 10 | return _SpeakPageState(); 11 | } 12 | } 13 | 14 | class _SpeakPageState extends State 15 | with SingleTickerProviderStateMixin { 16 | String speakTips = "长按说话"; 17 | String speakResult = ""; 18 | Animation animation; 19 | AnimationController controller; 20 | 21 | @override 22 | void initState() { 23 | controller = AnimationController( 24 | vsync: this, duration: Duration(milliseconds: 1000)); 25 | animation = CurvedAnimation(parent: controller, curve: Curves.easeIn) 26 | ..addStatusListener((status) { 27 | //动画放大缩小 可以通过 28 | if (status == AnimationStatus.completed) { 29 | controller.reverse(); 30 | } else if (status == AnimationStatus.dismissed) { 31 | controller.forward(); 32 | } 33 | }); 34 | super.initState(); 35 | } 36 | 37 | @override 38 | void dispose() { 39 | controller.dispose(); 40 | super.dispose(); 41 | } 42 | 43 | @override 44 | Widget build(BuildContext context) { 45 | return Scaffold( 46 | body: Container( 47 | padding: EdgeInsets.all(30), 48 | child: Center( 49 | child: Column( 50 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 51 | children: [ 52 | _topItem(), 53 | _bottomItem(), 54 | ], 55 | ), 56 | ), 57 | ), 58 | ); 59 | } 60 | 61 | _topItem() { 62 | return Column( 63 | children: [ 64 | Padding( 65 | padding: EdgeInsets.fromLTRB(0, 20, 0, 30), 66 | child: Text( 67 | "你可以这样说", 68 | style: TextStyle(fontSize: 16, color: Colors.black54), 69 | ), 70 | ), 71 | Text( 72 | "故宫门票\n背景一日游\n迪士尼乐园", 73 | textAlign: TextAlign.center, 74 | style: TextStyle(fontSize: 15, color: Colors.grey), 75 | ), 76 | Padding( 77 | padding: EdgeInsets.all(20), 78 | child: Text( 79 | speakResult, 80 | style: TextStyle(color: Colors.blue), 81 | ), 82 | ), 83 | ], 84 | ); 85 | } 86 | 87 | _speakStart() { 88 | controller.forward(); 89 | setState(() { 90 | speakTips = '- 识别中 -'; 91 | }); 92 | AsrManager.start().then((text) { 93 | if (text != null && text.length > 0) { 94 | setState(() { 95 | speakResult = text; 96 | }); 97 | //先关闭页面再跳转 98 | Navigator.pop(context); 99 | NavigatorUtil.push(context, SearchPage( 100 | keyword: speakResult, 101 | )); 102 | } 103 | }).catchError((e){ 104 | print("-----" + e.toString()); 105 | }); 106 | } 107 | 108 | _speakStop() { 109 | setState(() { 110 | speakTips = "长按说话"; 111 | }); 112 | controller.reset(); 113 | controller.stop(); 114 | AsrManager.stop(); 115 | } 116 | 117 | _bottomItem() { 118 | return FractionallySizedBox( 119 | //宽度撑满屏幕 120 | widthFactor: 1, 121 | child: Stack( 122 | children: [ 123 | GestureDetector( 124 | onTapDown: (e) { 125 | _speakStart(); 126 | }, 127 | onTapUp: (e) { 128 | _speakStop(); 129 | }, 130 | onTapCancel: () { 131 | _speakStop(); 132 | }, 133 | child: Center( 134 | child: Column( 135 | children: [ 136 | Padding( 137 | padding: EdgeInsets.all(10), 138 | child: Text( 139 | speakTips, 140 | style: TextStyle(color: Colors.blue, fontSize: 12), 141 | ), 142 | ), 143 | Stack( 144 | //为了按钮放大缩小时不改变上面布局的大小 145 | children: [ 146 | Container( 147 | height: MIC_SIZE, 148 | width: MIC_SIZE, 149 | ), 150 | Center( 151 | child: AnimatedMic( 152 | animation: animation, 153 | ), 154 | ), 155 | ], 156 | ), 157 | ], 158 | ), 159 | ), 160 | ), 161 | Positioned( 162 | right: 0, 163 | bottom: 20, 164 | child: GestureDetector( 165 | onTap: () { 166 | Navigator.pop(context); 167 | }, 168 | child: Icon( 169 | Icons.close, 170 | size: 30, 171 | color: Colors.grey, 172 | ), 173 | ), 174 | ), 175 | ], 176 | ), 177 | ); 178 | } 179 | } 180 | 181 | const double MIC_SIZE = 80; 182 | 183 | class AnimatedMic extends AnimatedWidget { 184 | static final _opacityTween = Tween(begin: 1, end: 0.5); 185 | static final _sizeTween = Tween(begin: MIC_SIZE, end: MIC_SIZE - 20); 186 | 187 | AnimatedMic({Key key, Animation animation}) 188 | : super(key: key, listenable: animation); 189 | 190 | @override 191 | Widget build(BuildContext context) { 192 | final Animation animation = listenable; 193 | return Opacity( 194 | opacity: _opacityTween.evaluate(animation), 195 | child: Container( 196 | height: _sizeTween.evaluate(animation), 197 | width: _sizeTween.evaluate(animation), 198 | decoration: BoxDecoration( 199 | color: Colors.blue, 200 | borderRadius: BorderRadius.circular(MIC_SIZE / 2), 201 | ), 202 | child: Icon( 203 | Icons.mic, 204 | color: Colors.white, 205 | size: 30, 206 | ), 207 | ), 208 | ); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /lib/pages/travel_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:xiechengwang_app/dao/travel_tab_dao.dart'; 3 | import 'package:xiechengwang_app/model/travel_tab_model.dart'; 4 | import 'package:xiechengwang_app/pages/travel_tab_page.dart'; 5 | 6 | class TravelPage extends StatefulWidget { 7 | @override 8 | _TravelPageState createState() { 9 | // TODO: implement createState 10 | return _TravelPageState(); 11 | } 12 | } 13 | 14 | class _TravelPageState extends State 15 | with TickerProviderStateMixin { 16 | TabController _controller; 17 | List tabs = []; 18 | TravelTabModel travelTabModel; 19 | 20 | @override 21 | void initState() { 22 | _controller = TabController(length: 0, vsync: this); 23 | TravelTabDao.fetch().then((TravelTabModel model) { 24 | //解决顶部tab空白问题 25 | _controller = TabController(length: model.tabs.length, vsync: this); 26 | setState(() { 27 | tabs = model.tabs; 28 | travelTabModel = model; 29 | }); 30 | }).catchError((e) { 31 | print(e); 32 | }); 33 | super.initState(); 34 | } 35 | 36 | @override 37 | void dispose() { 38 | _controller.dispose(); 39 | super.dispose(); 40 | } 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | return Scaffold( 45 | body: Column( 46 | children: [ 47 | Container( 48 | color: Colors.white, 49 | padding: EdgeInsets.only(top: 30), 50 | child: TabBar( 51 | controller: _controller, 52 | isScrollable: true, 53 | labelColor: Colors.black, 54 | labelPadding: EdgeInsets.fromLTRB(20, 0, 10, 5), 55 | indicator: UnderlineTabIndicator( 56 | borderSide: BorderSide(color: Color(0xff2fcfbb), width: 3), 57 | insets: EdgeInsets.only(bottom: 10), 58 | ), 59 | tabs: tabs.map((TravelTab tab) { 60 | return Tab( 61 | text: tab.labelName, 62 | ); 63 | }).toList(), 64 | ), 65 | ), 66 | Flexible( 67 | child: TabBarView( 68 | //会有异常 需用Flexible撑开 69 | controller: _controller, 70 | children: tabs.map((TravelTab tab) { 71 | return TravelTabPage( 72 | travelUrl: travelTabModel.url, 73 | groupChannelCode: tab.groupChannelCode, 74 | ); 75 | }).toList(), 76 | ), 77 | ), 78 | ], 79 | ), 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/pages/travel_tab_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; 3 | import 'package:xiechengwang_app/dao/travel_dao.dart'; 4 | import 'package:xiechengwang_app/model/travel_model.dart'; 5 | import 'package:xiechengwang_app/util/navigator_util.dart'; 6 | import 'package:xiechengwang_app/widget/loading_container.dart'; 7 | import 'package:xiechengwang_app/widget/webview.dart'; 8 | 9 | const _TRAVEL_URL = 10 | "https://m.ctrip.com/restapi/soa2/16189/json/searchTripShootListForHomePageV2?_fxpcqlniredt=09031014111431397988&__gw_appid=99999999&__gw_ver=1.0&__gw_from=10650013707&__gw_platform=H5"; 11 | const PAGE_SIZE = 10; 12 | 13 | class TravelTabPage extends StatefulWidget { 14 | final String travelUrl; 15 | final String groupChannelCode; 16 | 17 | const TravelTabPage({Key key, this.travelUrl, this.groupChannelCode}) 18 | : super(key: key); 19 | 20 | @override 21 | _TravelTabPageState createState() { 22 | // TODO: implement createState 23 | return _TravelTabPageState(); 24 | } 25 | } 26 | 27 | class _TravelTabPageState extends State 28 | with AutomaticKeepAliveClientMixin { 29 | List travelItems = []; 30 | int pageIndex = 1; 31 | bool isLoading = true; 32 | ScrollController _scrollController = ScrollController(); 33 | 34 | @override 35 | void initState() { 36 | _loadData(); 37 | _scrollController.addListener((){ 38 | if(_scrollController.position.pixels == _scrollController.position.maxScrollExtent){ 39 | _loadData(loadMore: true); 40 | } 41 | }); 42 | super.initState(); 43 | } 44 | @override 45 | void dispose() { 46 | _scrollController.dispose(); 47 | super.dispose(); 48 | } 49 | void _loadData({loadMore=false}) { 50 | if(loadMore){ 51 | pageIndex ++; 52 | }else{ 53 | pageIndex = 1; 54 | } 55 | TravelDao.fetch(widget.travelUrl ?? _TRAVEL_URL, widget.groupChannelCode, 56 | pageIndex, PAGE_SIZE) 57 | .then((model) { 58 | setState(() { 59 | isLoading = false; 60 | List items = _filterItems(model.resultList); 61 | if (items != null) { 62 | travelItems.addAll(items); 63 | } else { 64 | travelItems = items; 65 | } 66 | }); 67 | }).catchError((e) { 68 | isLoading = false; 69 | print(e); 70 | }); 71 | } 72 | 73 | Future _handleRefresh() async { 74 | _loadData(); 75 | return null; 76 | } 77 | 78 | @override 79 | Widget build(BuildContext context) { 80 | return Scaffold( 81 | body: LoadingContainer( 82 | isLoading: isLoading, 83 | child: RefreshIndicator( 84 | onRefresh: _handleRefresh, 85 | child: MediaQuery.removePadding( 86 | context: context, 87 | removeTop: true, 88 | child: StaggeredGridView.countBuilder( 89 | controller: _scrollController, 90 | crossAxisCount: 2, 91 | itemCount: travelItems?.length ?? 0, 92 | itemBuilder: (BuildContext context, int index) => 93 | _TravelItem(index: index, item: travelItems[index]), 94 | staggeredTileBuilder: (int index) => new StaggeredTile.fit(1), 95 | mainAxisSpacing: 4.0, 96 | crossAxisSpacing: 4.0, 97 | ), 98 | ), 99 | ), 100 | ), 101 | ); 102 | } 103 | 104 | @override 105 | bool get wantKeepAlive => true; 106 | } 107 | 108 | List _filterItems(List resultList) { 109 | if (resultList == null) { 110 | return []; 111 | } 112 | List filterItems = []; 113 | resultList.forEach((item) { 114 | if (item.article != null) { 115 | filterItems.add(item); 116 | } 117 | }); 118 | return filterItems; 119 | } 120 | 121 | class _TravelItem extends StatelessWidget { 122 | final TravelItem item; 123 | final int index; 124 | 125 | const _TravelItem({Key key, this.item, this.index}) : super(key: key); 126 | 127 | @override 128 | Widget build(BuildContext context) { 129 | return GestureDetector( 130 | onTap: () { 131 | if (item.article.urls != null && item.article.urls.length > 0) { 132 | NavigatorUtil.push(context, WebView(url: item.article.urls[0].h5Url, title: "详情")); 133 | } 134 | }, 135 | child: Card( 136 | child: PhysicalModel( 137 | color: Colors.transparent, 138 | clipBehavior: Clip.antiAlias, 139 | borderRadius: BorderRadius.circular(5), 140 | child: Column( 141 | crossAxisAlignment: CrossAxisAlignment.start, 142 | children: [ 143 | _itemImage(), 144 | Container( 145 | padding: EdgeInsets.all(4), 146 | child: Text( 147 | item.article.articleTitle, 148 | maxLines: 2, 149 | overflow: TextOverflow.ellipsis, 150 | style: TextStyle(fontSize: 14, color: Colors.black87), 151 | ), 152 | ), 153 | _infoText(), 154 | ], 155 | ), 156 | ), 157 | ), 158 | ); 159 | } 160 | 161 | Widget _itemImage() { 162 | return Stack( 163 | children: [ 164 | Image.network(item.article.images[0]?.dynamicUrl), 165 | Positioned( 166 | bottom: 8, 167 | left: 8, 168 | child: Container( 169 | padding: EdgeInsets.fromLTRB(5, 1, 5, 1), 170 | decoration: BoxDecoration( 171 | color: Colors.black54, borderRadius: BorderRadius.circular(10)), 172 | child: Row( 173 | children: [ 174 | Padding( 175 | padding: EdgeInsets.only(right: 3), 176 | child: Icon( 177 | Icons.location_on, 178 | color: Colors.white, 179 | size: 12, 180 | ), 181 | ), 182 | LimitedBox( 183 | maxWidth: 130, 184 | child: Text( 185 | _poiName(), 186 | maxLines: 1, 187 | overflow: TextOverflow.ellipsis, 188 | style: TextStyle(color: Colors.white, fontSize: 12), 189 | ), 190 | ), 191 | ], 192 | ), 193 | ), 194 | ), 195 | ], 196 | ); 197 | } 198 | 199 | Widget _infoText() { 200 | return Container( 201 | padding: EdgeInsets.fromLTRB(6, 0, 6, 10), 202 | child: Row( 203 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 204 | children: [ 205 | Row( 206 | children: [ 207 | PhysicalModel( 208 | color: Colors.transparent, 209 | clipBehavior: Clip.antiAlias, 210 | borderRadius: BorderRadius.circular(12), 211 | child: Image.network( 212 | item.article.author?.coverImage?.dynamicUrl, 213 | width: 24, 214 | height: 24, 215 | ), 216 | ), 217 | Container( 218 | padding: EdgeInsets.all(5), 219 | width: 90, 220 | child: Text( 221 | item.article.author?.nickName, 222 | maxLines: 1, 223 | overflow: TextOverflow.ellipsis, 224 | style: TextStyle(fontSize: 12), 225 | ), 226 | ) 227 | ], 228 | ), 229 | Row( 230 | children: [ 231 | Icon( 232 | Icons.thumb_up, 233 | size: 14, 234 | color: Colors.grey, 235 | ), 236 | Padding( 237 | padding: EdgeInsets.only(left: 3), 238 | child: Text( 239 | item.article.likeCount.toString(), 240 | style: TextStyle(fontSize: 10), 241 | ), 242 | ), 243 | ], 244 | ), 245 | ], 246 | ), 247 | ); 248 | } 249 | 250 | String _poiName() { 251 | return item.article.pois == null || item.article.pois.length == 0 252 | ? "未知" 253 | : item.article.pois[0].poiName ?? "未知"; 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /lib/plugin/asr_manager.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | 3 | class AsrManager { 4 | static const MethodChannel _channel = const MethodChannel("asr_plugin"); 5 | 6 | //开始录音 7 | static Future start({Map params}) async { 8 | return await _channel.invokeMethod("start", params ?? {}); 9 | } 10 | 11 | //停止录音 12 | static Future stop() async { 13 | return await _channel.invokeMethod("stop"); 14 | } 15 | 16 | //取消录音 17 | static Future cancel() async { 18 | return await _channel.invokeMethod("cancel"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/util/navigator_util.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class NavigatorUtil { 5 | ///跳转到指定页面 6 | static push(BuildContext context, Widget page) { 7 | Navigator.push(context, MaterialPageRoute(builder: (context) => page)); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/widget/grid_nav.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:xiechengwang_app/model/common_model.dart'; 3 | import 'package:xiechengwang_app/model/grid_nav_model.dart'; 4 | import 'package:xiechengwang_app/util/navigator_util.dart'; 5 | import 'package:xiechengwang_app/widget/webview.dart'; 6 | 7 | //网格布局 8 | class GridNav extends StatelessWidget { 9 | final GridNavModel gridNavModel; 10 | 11 | const GridNav({Key key, @required this.gridNavModel}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return PhysicalModel( 16 | //圆角彩蛋 17 | color: Colors.transparent, 18 | borderRadius: BorderRadius.circular(6), 19 | clipBehavior: Clip.antiAlias, 20 | child: Column( 21 | children: _gridNavItems(context), 22 | ), 23 | ); 24 | } 25 | 26 | _gridNavItems(BuildContext context) { 27 | List items = []; 28 | if (gridNavModel == null) { 29 | return items; 30 | } 31 | if (gridNavModel.hotel != null) { 32 | items.add(_gridNavItem(context, gridNavModel.hotel, true)); 33 | } 34 | if (gridNavModel.flight != null) { 35 | items.add(_gridNavItem(context, gridNavModel.flight, false)); 36 | } 37 | if (gridNavModel.travel != null) { 38 | items.add(_gridNavItem(context, gridNavModel.travel, false)); 39 | } 40 | return items; 41 | } 42 | 43 | //每行卡片组成 左 main item + 中 item1 + 右 item2 44 | _gridNavItem(BuildContext context, GridNavItem gridNavItem, bool first) { 45 | List items = []; 46 | 47 | items.add(_mainItem(context, gridNavItem.mainItem)); 48 | items.add(_doubleItem(context, gridNavItem.item1, gridNavItem.item2)); 49 | items.add(_doubleItem(context, gridNavItem.item3, gridNavItem.item4)); 50 | List expandItems = []; 51 | items.forEach((item) { 52 | expandItems.add(Expanded( 53 | child: item, 54 | flex: 1, 55 | )); 56 | }); 57 | Color startColor = Color(int.parse("0xff" + gridNavItem.startColor)); 58 | Color endColor = Color(int.parse("0xff" + gridNavItem.endColor)); 59 | return Container( 60 | height: 88, 61 | margin: first ? null : EdgeInsets.only(top: 3), 62 | decoration: BoxDecoration( 63 | //线性渐变 64 | gradient: LinearGradient(colors: [startColor, endColor])), 65 | child: Row( 66 | children: expandItems, 67 | ), 68 | ); 69 | } 70 | 71 | // 72 | _mainItem(BuildContext context, CommonModel model) { 73 | return _wrapGesture( 74 | context, 75 | Stack( 76 | alignment: Alignment.topCenter, 77 | children: [ 78 | Image.network( 79 | model.icon, 80 | fit: BoxFit.contain, 81 | height: 88, 82 | width: 121, 83 | alignment: AlignmentDirectional.bottomEnd, 84 | ), 85 | Container( 86 | margin: EdgeInsets.only(top: 12), 87 | child: Text( 88 | model.title, 89 | style: TextStyle(fontSize: 14, color: Colors.white), 90 | ), 91 | ), 92 | ], 93 | ), 94 | model); 95 | } 96 | 97 | _doubleItem( 98 | BuildContext context, 99 | CommonModel topItem, 100 | CommonModel bottomItem, 101 | ) { 102 | return Column( 103 | children: [ 104 | Expanded( 105 | //高 展开 106 | child: _item(context, topItem, true), 107 | ), 108 | Expanded( 109 | child: _item(context, bottomItem, false), 110 | ), 111 | ], 112 | ); 113 | } 114 | 115 | _item(BuildContext context, CommonModel item, bool first) { 116 | BorderSide borderSide = BorderSide(width: 0.8, color: Colors.white); 117 | return FractionallySizedBox( 118 | widthFactor: 1, 119 | child: Container( 120 | decoration: BoxDecoration( 121 | border: Border( 122 | left: borderSide, 123 | bottom: first ? borderSide : BorderSide.none, //第一个底部有分割线 124 | )), 125 | child: _wrapGesture( 126 | context, 127 | Center( 128 | child: Text( 129 | item.title, 130 | textAlign: TextAlign.center, 131 | style: TextStyle(fontSize: 14, color: Colors.white), 132 | ), 133 | ), 134 | item)), 135 | ); 136 | } 137 | 138 | _wrapGesture(BuildContext context, Widget widget, CommonModel model) { 139 | return GestureDetector( 140 | onTap: () { 141 | NavigatorUtil.push( 142 | context, 143 | WebView( 144 | url: model.url, 145 | statusBarColor: model.statusBarColor, 146 | hideAppBar: model.hideAppBar, 147 | )); 148 | }, 149 | child: widget, 150 | ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/widget/loading_container.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | //加载进度条 6 | class LoadingContainer extends StatelessWidget{ 7 | final Widget child; 8 | final bool isLoading; 9 | final bool cover;//是否放在顶层 10 | 11 | const LoadingContainer({Key key,@required this.child, @required this.isLoading, this.cover = false}):super(key:key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | // TODO: implement build 16 | return !cover ? !isLoading ?child : _loadingView : Stack( 17 | children: [ 18 | child, 19 | isLoading ?_loadingView : null, 20 | ], 21 | ); 22 | } 23 | 24 | Widget get _loadingView { 25 | return Center( 26 | child: CircularProgressIndicator(), 27 | ); 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /lib/widget/local_nav.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:xiechengwang_app/model/common_model.dart'; 5 | import 'package:xiechengwang_app/util/navigator_util.dart'; 6 | import 'package:xiechengwang_app/widget/webview.dart'; 7 | 8 | class LocalNav extends StatelessWidget{ 9 | final List localNavList; 10 | 11 | const LocalNav({Key key,@required this.localNavList}):super(key:key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | // TODO: implement build 16 | return Container( 17 | height: 64, 18 | decoration: BoxDecoration( 19 | color: Colors.white, 20 | borderRadius: BorderRadius.all(Radius.circular(6)), 21 | ), 22 | child: Padding( 23 | padding: EdgeInsets.all(7), 24 | child: _items(context), 25 | ), 26 | ); 27 | } 28 | 29 | _items(BuildContext context){ 30 | if(localNavList == null){ 31 | return null; 32 | } 33 | List items = []; 34 | localNavList.forEach((model)=> items.add(_item(context, model))); 35 | 36 | return Row( 37 | mainAxisAlignment: MainAxisAlignment.spaceAround,//平均排列 38 | children:items, 39 | ); 40 | } 41 | Widget _item(BuildContext context,CommonModel model){ 42 | return GestureDetector( 43 | onTap: (){ 44 | NavigatorUtil.push(context, WebView(url:model.url,statusBarColor: model.statusBarColor,hideAppBar: model.hideAppBar,)); 45 | }, 46 | child: Column( 47 | children: [ 48 | Image.network( 49 | model.icon, 50 | width: 32, 51 | height: 32, 52 | ), 53 | Text( 54 | model.title, 55 | style: TextStyle(fontSize: 12), 56 | ) 57 | ], 58 | ), 59 | ); 60 | } 61 | } -------------------------------------------------------------------------------- /lib/widget/sales_box.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:xiechengwang_app/model/common_model.dart'; 4 | import 'package:xiechengwang_app/model/sales_box_model.dart'; 5 | import 'package:xiechengwang_app/util/navigator_util.dart'; 6 | import 'package:xiechengwang_app/widget/webview.dart'; 7 | 8 | //底部卡片入口 9 | class SalesBox extends StatelessWidget { 10 | final SalesBoxModel salesBox; 11 | 12 | const SalesBox({Key key, @required this.salesBox}) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Container( 17 | decoration: BoxDecoration( 18 | color: Colors.white, 19 | ), 20 | child: _items(context), 21 | ); 22 | } 23 | 24 | _items(BuildContext context) { 25 | if (salesBox == null) { 26 | return null; 27 | } 28 | List items = []; 29 | items.add(_doubleItem(context,salesBox.bigCard1,salesBox.bigCard2,true,false)); 30 | items.add(_doubleItem(context,salesBox.smallCard1,salesBox.smallCard2,false,false)); 31 | items.add(_doubleItem(context,salesBox.smallCard3,salesBox.smallCard4,false,true)); 32 | 33 | 34 | return Column( 35 | children: [ 36 | Container( 37 | height: 44, 38 | margin: EdgeInsets.only(left: 10), 39 | decoration: BoxDecoration( 40 | border: Border(bottom: BorderSide(width: 1,color: Color(0xfff2f2))), //下划线 41 | ), 42 | child: Row( 43 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 44 | children: [ 45 | Image.network(salesBox.icon,height: 15,fit: BoxFit.fill,), 46 | Container( 47 | padding: EdgeInsets.fromLTRB(10,1,8,1), 48 | margin: EdgeInsets.only(right: 7), 49 | decoration: BoxDecoration( 50 | borderRadius: BorderRadius.circular(12), 51 | gradient: LinearGradient( 52 | colors: [Color(0xffff4e63),Color(0xffff6cc9)], 53 | begin: Alignment.centerLeft, 54 | end: Alignment.centerRight 55 | ) 56 | ), 57 | child: GestureDetector( 58 | onTap: (){ 59 | NavigatorUtil.push(context, WebView(url: salesBox.moreUrl,title: "更多活动",)); 60 | }, 61 | child: Text('获取更多福利 >',style: TextStyle(color: Colors.white,fontSize: 12),), 62 | ), 63 | ) 64 | ], 65 | ), 66 | ), 67 | 68 | Row( 69 | mainAxisAlignment: MainAxisAlignment.center, 70 | children: items.sublist(0,1), 71 | ), 72 | Row( 73 | mainAxisAlignment: MainAxisAlignment.center, 74 | children: items.sublist(1,2), 75 | ), 76 | Row(mainAxisAlignment: MainAxisAlignment.center, 77 | children: items.sublist(2,3), 78 | ), 79 | 80 | ], 81 | ); 82 | } 83 | 84 | _doubleItem(BuildContext context,CommonModel leftCard,CommonModel rightCard,bool big,bool last){ 85 | return Row( 86 | mainAxisAlignment: MainAxisAlignment.spaceAround, 87 | children: [ 88 | _item(context, leftCard,big,true,last), 89 | _item(context, rightCard,big,false,last), 90 | ], 91 | ); 92 | } 93 | 94 | Widget _item(BuildContext context, CommonModel model,bool big,bool left,bool last) { 95 | BorderSide borderSide = BorderSide(width: 0.8,color: Color(0xfff2f2)); 96 | return GestureDetector( 97 | onTap: () { 98 | NavigatorUtil.push(context, WebView( 99 | url: model.url, 100 | statusBarColor: model.statusBarColor, 101 | hideAppBar: model.hideAppBar, 102 | )); 103 | }, 104 | child: Container( 105 | decoration: BoxDecoration( 106 | border: Border(right: left ? borderSide :BorderSide.none, 107 | bottom: last ? BorderSide.none : borderSide), 108 | 109 | ), 110 | child: Image.network( 111 | model.icon, 112 | fit: BoxFit.fill, 113 | width: MediaQuery.of(context).size.width / 2 - 10, 114 | height: big ? 129 : 80, 115 | ), 116 | ), 117 | 118 | ); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /lib/widget/search_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | enum SearchBarType { home, normal, homeLight } 5 | 6 | class SearchBar extends StatefulWidget { 7 | final bool enabled; 8 | final bool hideLeft; 9 | final SearchBarType searchBarType; 10 | final String hint; 11 | final String defaultText; 12 | final void Function() leftButtonClick; 13 | final void Function() rightButtonClick; 14 | final void Function() speakClick; 15 | final void Function() inputBoxClick; 16 | final ValueChanged onChanged; 17 | 18 | const SearchBar( 19 | {Key key, 20 | this.enabled = true, 21 | this.hideLeft, 22 | this.searchBarType = SearchBarType.normal, 23 | this.hint, 24 | this.defaultText, 25 | this.leftButtonClick, 26 | this.rightButtonClick, 27 | this.speakClick, 28 | this.inputBoxClick, 29 | this.onChanged}) 30 | : super(key: key); 31 | 32 | @override 33 | _SearchBarState createState() { 34 | return _SearchBarState(); 35 | } 36 | } 37 | 38 | class _SearchBarState extends State { 39 | bool showClear = false; 40 | final TextEditingController _controller = TextEditingController(); 41 | 42 | @override 43 | void initState() { 44 | super.initState(); 45 | if (widget.defaultText != null) { 46 | setState(() { 47 | _controller.text = widget.defaultText; 48 | }); 49 | } 50 | } 51 | 52 | @override 53 | Widget build(BuildContext context) { 54 | return widget.searchBarType == SearchBarType.normal 55 | ? _genNormalSearch() 56 | : _genHomeSearch(); 57 | } 58 | 59 | _genNormalSearch() { 60 | return Container( 61 | child: Row( 62 | children: [ 63 | _wrapTap( 64 | Container( 65 | padding: EdgeInsets.fromLTRB(6, 5, 6, 5), 66 | child: widget?.hideLeft ?? false 67 | ? null 68 | : Icon( 69 | Icons.arrow_back_ios, 70 | color: Colors.grey, 71 | size: 26, 72 | ), 73 | ), 74 | widget.leftButtonClick), 75 | Expanded( 76 | flex: 1, 77 | child: _inputBox(), 78 | ), 79 | _wrapTap( 80 | Container( 81 | padding: EdgeInsets.fromLTRB(10, 5, 10, 5), 82 | child: Text( 83 | '搜索', 84 | style: TextStyle(color: Colors.blue, fontSize: 17), 85 | ), 86 | ), 87 | widget.rightButtonClick), 88 | ], 89 | ), 90 | ); 91 | } 92 | 93 | _inputBox() { 94 | Color inputBoxColor; 95 | if (widget.searchBarType == SearchBarType.home) { 96 | inputBoxColor = Colors.white; 97 | } else { 98 | inputBoxColor = Color(int.parse('0xffEDEDED')); 99 | } 100 | return Container( 101 | height: 30, 102 | padding: EdgeInsets.fromLTRB(10, 0, 10, 0), 103 | decoration: BoxDecoration( 104 | color: inputBoxColor, 105 | borderRadius: BorderRadius.circular(widget.searchBarType == SearchBarType.normal ? 5 : 15), 106 | ), 107 | child: Row( 108 | children: [ 109 | Icon( 110 | Icons.search, 111 | size: 20, 112 | color: widget.searchBarType == SearchBarType.normal 113 | ? Color(0xffA9A9A9) 114 | : Colors.blue, 115 | ), 116 | Expanded( 117 | flex: 1, 118 | child: widget.searchBarType == SearchBarType.normal 119 | ? TextField( 120 | controller: _controller, 121 | onChanged: _onChanged, 122 | autofocus: true, 123 | style: TextStyle( 124 | fontSize: 18.0, 125 | color: Colors.black, 126 | fontWeight: FontWeight.w300), 127 | //输入文本的样式 128 | decoration: InputDecoration( 129 | contentPadding: EdgeInsets.fromLTRB(5, 0, 5, 0), 130 | border: InputBorder.none, 131 | hintText: widget.hint ?? "", 132 | hintStyle: TextStyle(fontSize: 15), 133 | ), 134 | ) 135 | : _wrapTap( 136 | Container( 137 | child: Text( 138 | widget.defaultText, 139 | style: TextStyle(fontSize: 13, color: Colors.grey), 140 | ), 141 | ), 142 | widget.inputBoxClick), 143 | ), 144 | !showClear ? _wrapTap(Icon(Icons.mic,size: 22,color: widget.searchBarType == SearchBarType.normal ? Colors.blue:Colors.grey,), widget.speakClick) 145 | : _wrapTap(Icon(Icons.clear,size: 22,color: Colors.grey,), (){ 146 | setState(() { 147 | _controller.clear(); 148 | }); 149 | _onChanged(""); 150 | }) 151 | ], 152 | ), 153 | ); 154 | } 155 | 156 | _genHomeSearch() { 157 | return Container( 158 | child: Row( 159 | children: [ 160 | _wrapTap( 161 | Container( 162 | padding: EdgeInsets.fromLTRB(6, 5, 5, 5), 163 | child:Row( 164 | children: [ 165 | Text('上海',style: TextStyle(color: _homeFontColor(),fontSize: 14),), 166 | Icon(Icons.expand_more,color: _homeFontColor(),size: 22,) 167 | ], 168 | ), 169 | ), 170 | widget.leftButtonClick), 171 | Expanded( 172 | flex: 1, 173 | child: _inputBox(), 174 | ), 175 | _wrapTap( 176 | Container( 177 | padding: EdgeInsets.fromLTRB(10, 5, 10, 5), 178 | child: Icon(Icons.comment,color: _homeFontColor(),size: 26,) 179 | ), 180 | widget.rightButtonClick), 181 | ], 182 | ), 183 | ); 184 | } 185 | 186 | _wrapTap(Widget child, void Function() callback) { 187 | return GestureDetector( 188 | onTap: () { 189 | if (callback != null) { 190 | callback(); 191 | } 192 | }, 193 | child: child, 194 | ); 195 | } 196 | 197 | _onChanged(String text) { 198 | if (text.length > 0) { 199 | setState(() { 200 | showClear = true; 201 | }); 202 | } else { 203 | setState(() { 204 | showClear = false; 205 | }); 206 | } 207 | if (widget.onChanged != null) { 208 | widget.onChanged(text); 209 | } 210 | } 211 | _homeFontColor(){ 212 | return widget.searchBarType == SearchBarType.homeLight ? Colors.black54 : Colors.white; 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /lib/widget/sub_nav.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:xiechengwang_app/model/common_model.dart'; 4 | import 'package:xiechengwang_app/util/navigator_util.dart'; 5 | import 'package:xiechengwang_app/widget/webview.dart'; 6 | 7 | class SubNav extends StatelessWidget { 8 | final List subNavList; 9 | 10 | const SubNav({Key key, @required this.subNavList}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | // TODO: implement build 15 | return Container( 16 | decoration: BoxDecoration( 17 | color: Colors.white, 18 | borderRadius: BorderRadius.all(Radius.circular(6)), 19 | ), 20 | child: Padding( 21 | padding: EdgeInsets.all(7), 22 | child: _items(context), 23 | ), 24 | ); 25 | } 26 | 27 | _items(BuildContext context) { 28 | if (subNavList == null) { 29 | return null; 30 | } 31 | List items = []; 32 | subNavList.forEach((model) => items.add(_item(context, model))); 33 | 34 | int separate = (subNavList.length / 2 + 0.5).toInt(); 35 | 36 | return Column( 37 | children: [ 38 | Row( 39 | mainAxisAlignment: MainAxisAlignment.spaceAround, //平均排列 40 | children: items.sublist(0, separate), 41 | ), 42 | Padding( 43 | padding: EdgeInsets.only(top: 10), 44 | child: Row( 45 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 46 | children: items.sublist(separate, subNavList.length), 47 | ), 48 | ), 49 | ], 50 | ); 51 | } 52 | 53 | Widget _item(BuildContext context, CommonModel model) { 54 | return Expanded( 55 | flex: 1, 56 | child: GestureDetector( 57 | onTap: () { 58 | NavigatorUtil.push(context, WebView( 59 | url: model.url, 60 | statusBarColor: model.statusBarColor, 61 | hideAppBar: model.hideAppBar, 62 | )); 63 | }, 64 | child: Column( 65 | children: [ 66 | Image.network( 67 | model.icon, 68 | width: 18, 69 | height: 18, 70 | ), 71 | Padding( 72 | padding: EdgeInsets.only(top: 3), 73 | child: Text( 74 | model.title, 75 | style: TextStyle(fontSize: 12), 76 | )), 77 | ], 78 | ), 79 | ), 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/widget/webview.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 6 | 7 | const CATCH_URLS = ['m.ctrip.com/','m.ctrip.com/html5/','m.ctrip.com/html5']; 8 | 9 | class WebView extends StatefulWidget { 10 | final String url; 11 | final String statusBarColor; 12 | final String title; 13 | final bool hideAppBar; 14 | final bool backForbid; 15 | 16 | WebView( 17 | {this.url, 18 | this.statusBarColor, 19 | this.title, 20 | this.hideAppBar, 21 | this.backForbid = false}); 22 | 23 | @override 24 | State createState() { 25 | // TODO: implement createState 26 | return _WebViewState(); 27 | } 28 | } 29 | 30 | class _WebViewState extends State { 31 | final webViewReference = FlutterWebviewPlugin(); 32 | StreamSubscription _onUrlChanged; 33 | StreamSubscription _onStateChanged; 34 | StreamSubscription _onHttpError; 35 | 36 | bool exiting = false; 37 | @override 38 | void initState() { 39 | super.initState(); 40 | webViewReference.close(); 41 | _onUrlChanged = webViewReference.onUrlChanged.listen((String url) {}); 42 | _onStateChanged = 43 | webViewReference.onStateChanged.listen((WebViewStateChanged state) { 44 | switch (state.type) { 45 | case WebViewState.startLoad: 46 | //防止返回到上一主页 携程首页 47 | if(_isToMain(state.url) && !exiting){ 48 | if(widget.backForbid){ 49 | //禁止返回到主页面 50 | webViewReference.launch(widget.url); 51 | }else{ 52 | Navigator.pop(context); 53 | exiting = true; 54 | } 55 | } 56 | break; 57 | default: 58 | break; 59 | } 60 | }); 61 | _onHttpError = 62 | webViewReference.onHttpError.listen((WebViewHttpError error) { 63 | print(error.toString()); 64 | }); 65 | } 66 | 67 | _isToMain(String url){ 68 | bool contain = false; 69 | for(final value in CATCH_URLS){ 70 | if(url?.endsWith(value) ?? false){ 71 | contain = true; 72 | break; 73 | } 74 | } 75 | return contain; 76 | } 77 | @override 78 | void dispose() { 79 | _onHttpError.cancel(); 80 | _onStateChanged.cancel(); 81 | _onUrlChanged.cancel(); //取消注册监听 82 | webViewReference.dispose(); 83 | super.dispose(); 84 | } 85 | 86 | @override 87 | Widget build(BuildContext context) { 88 | String statusBarColorStr = widget.statusBarColor ?? "ffffff"; 89 | Color backButtonColor; 90 | if(statusBarColorStr == "ffffff"){ 91 | backButtonColor = Colors.black; 92 | }else{ 93 | backButtonColor = Colors.white; 94 | } 95 | return Scaffold( 96 | body: Column( 97 | children: [ 98 | _appBar(Color(int.parse("0xff"+statusBarColorStr)),backButtonColor), 99 | Expanded(child: WebviewScaffold( 100 | url: widget.url, 101 | withZoom: true, 102 | withLocalStorage: true, 103 | hidden: true, //有bug,设置隐藏后,initialChild不会显示 104 | initialChild: Container( 105 | color: Colors.white, 106 | child: Center( 107 | child: Text("Waiting...."), 108 | ), 109 | ), 110 | )), 111 | ], 112 | ), 113 | ); 114 | } 115 | 116 | _appBar(Color backgroundColor, Color backButtonColor) { 117 | if (widget.hideAppBar ?? false) { 118 | return Container( 119 | padding: EdgeInsets.fromLTRB(0, 40, 0, 10),//例如IphoneX 顶部刘海导致的问题 120 | color: backgroundColor, 121 | height: 30, 122 | ); 123 | } 124 | return Container( 125 | padding: EdgeInsets.fromLTRB(0, 40, 0, 10),//例如IphoneX 顶部刘海导致的问题 126 | color: backgroundColor, 127 | child: FractionallySizedBox( 128 | //撑满屏幕宽度 129 | widthFactor: 1, 130 | child: Stack( 131 | children: [ 132 | GestureDetector( 133 | onTap: (){ 134 | Navigator.pop(context); 135 | }, 136 | child: Container( 137 | margin: EdgeInsets.only(left: 10), 138 | child: Icon( 139 | Icons.close, 140 | color: backButtonColor, 141 | size: 26, 142 | ), 143 | ), 144 | ), 145 | Positioned( 146 | left: 0, 147 | right: 0, 148 | child: Center( 149 | child: Text( 150 | widget.title ?? "", 151 | style: TextStyle(color: backButtonColor,fontSize: 20), 152 | ), 153 | ), 154 | ) 155 | ], 156 | ), 157 | ), 158 | ); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | archive: 5 | dependency: transitive 6 | description: 7 | name: archive 8 | url: "https://pub.flutter-io.cn" 9 | source: hosted 10 | version: "2.0.10" 11 | args: 12 | dependency: transitive 13 | description: 14 | name: args 15 | url: "https://pub.flutter-io.cn" 16 | source: hosted 17 | version: "1.5.2" 18 | async: 19 | dependency: transitive 20 | description: 21 | name: async 22 | url: "https://pub.flutter-io.cn" 23 | source: hosted 24 | version: "2.3.0" 25 | boolean_selector: 26 | dependency: transitive 27 | description: 28 | name: boolean_selector 29 | url: "https://pub.flutter-io.cn" 30 | source: hosted 31 | version: "1.0.5" 32 | charcode: 33 | dependency: transitive 34 | description: 35 | name: charcode 36 | url: "https://pub.flutter-io.cn" 37 | source: hosted 38 | version: "1.1.2" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.flutter-io.cn" 44 | source: hosted 45 | version: "1.14.11" 46 | convert: 47 | dependency: transitive 48 | description: 49 | name: convert 50 | url: "https://pub.flutter-io.cn" 51 | source: hosted 52 | version: "2.1.1" 53 | crypto: 54 | dependency: transitive 55 | description: 56 | name: crypto 57 | url: "https://pub.flutter-io.cn" 58 | source: hosted 59 | version: "2.1.3" 60 | cupertino_icons: 61 | dependency: "direct main" 62 | description: 63 | name: cupertino_icons 64 | url: "https://pub.flutter-io.cn" 65 | source: hosted 66 | version: "0.1.3" 67 | flutter: 68 | dependency: "direct main" 69 | description: flutter 70 | source: sdk 71 | version: "0.0.0" 72 | flutter_page_indicator: 73 | dependency: transitive 74 | description: 75 | name: flutter_page_indicator 76 | url: "https://pub.flutter-io.cn" 77 | source: hosted 78 | version: "0.0.3" 79 | flutter_splash_screen: 80 | dependency: "direct main" 81 | description: 82 | name: flutter_splash_screen 83 | url: "https://pub.flutter-io.cn" 84 | source: hosted 85 | version: "0.1.0" 86 | flutter_staggered_grid_view: 87 | dependency: "direct main" 88 | description: 89 | name: flutter_staggered_grid_view 90 | url: "https://pub.flutter-io.cn" 91 | source: hosted 92 | version: "0.3.0" 93 | flutter_swiper: 94 | dependency: "direct main" 95 | description: 96 | name: flutter_swiper 97 | url: "https://pub.flutter-io.cn" 98 | source: hosted 99 | version: "1.1.6" 100 | flutter_test: 101 | dependency: "direct dev" 102 | description: flutter 103 | source: sdk 104 | version: "0.0.0" 105 | flutter_webview_plugin: 106 | dependency: "direct main" 107 | description: 108 | name: flutter_webview_plugin 109 | url: "https://pub.flutter-io.cn" 110 | source: hosted 111 | version: "0.3.10+1" 112 | http: 113 | dependency: "direct main" 114 | description: 115 | name: http 116 | url: "https://pub.flutter-io.cn" 117 | source: hosted 118 | version: "0.12.0+4" 119 | http_parser: 120 | dependency: transitive 121 | description: 122 | name: http_parser 123 | url: "https://pub.flutter-io.cn" 124 | source: hosted 125 | version: "3.1.3" 126 | image: 127 | dependency: transitive 128 | description: 129 | name: image 130 | url: "https://pub.flutter-io.cn" 131 | source: hosted 132 | version: "2.1.4" 133 | matcher: 134 | dependency: transitive 135 | description: 136 | name: matcher 137 | url: "https://pub.flutter-io.cn" 138 | source: hosted 139 | version: "0.12.5" 140 | meta: 141 | dependency: transitive 142 | description: 143 | name: meta 144 | url: "https://pub.flutter-io.cn" 145 | source: hosted 146 | version: "1.1.7" 147 | path: 148 | dependency: transitive 149 | description: 150 | name: path 151 | url: "https://pub.flutter-io.cn" 152 | source: hosted 153 | version: "1.6.4" 154 | pedantic: 155 | dependency: transitive 156 | description: 157 | name: pedantic 158 | url: "https://pub.flutter-io.cn" 159 | source: hosted 160 | version: "1.8.0+1" 161 | petitparser: 162 | dependency: transitive 163 | description: 164 | name: petitparser 165 | url: "https://pub.flutter-io.cn" 166 | source: hosted 167 | version: "2.4.0" 168 | quiver: 169 | dependency: transitive 170 | description: 171 | name: quiver 172 | url: "https://pub.flutter-io.cn" 173 | source: hosted 174 | version: "2.0.5" 175 | sky_engine: 176 | dependency: transitive 177 | description: flutter 178 | source: sdk 179 | version: "0.0.99" 180 | source_span: 181 | dependency: transitive 182 | description: 183 | name: source_span 184 | url: "https://pub.flutter-io.cn" 185 | source: hosted 186 | version: "1.5.5" 187 | stack_trace: 188 | dependency: transitive 189 | description: 190 | name: stack_trace 191 | url: "https://pub.flutter-io.cn" 192 | source: hosted 193 | version: "1.9.3" 194 | stream_channel: 195 | dependency: transitive 196 | description: 197 | name: stream_channel 198 | url: "https://pub.flutter-io.cn" 199 | source: hosted 200 | version: "2.0.0" 201 | string_scanner: 202 | dependency: transitive 203 | description: 204 | name: string_scanner 205 | url: "https://pub.flutter-io.cn" 206 | source: hosted 207 | version: "1.0.5" 208 | term_glyph: 209 | dependency: transitive 210 | description: 211 | name: term_glyph 212 | url: "https://pub.flutter-io.cn" 213 | source: hosted 214 | version: "1.1.0" 215 | test_api: 216 | dependency: transitive 217 | description: 218 | name: test_api 219 | url: "https://pub.flutter-io.cn" 220 | source: hosted 221 | version: "0.2.5" 222 | transformer_page_view: 223 | dependency: transitive 224 | description: 225 | name: transformer_page_view 226 | url: "https://pub.flutter-io.cn" 227 | source: hosted 228 | version: "0.1.6" 229 | typed_data: 230 | dependency: transitive 231 | description: 232 | name: typed_data 233 | url: "https://pub.flutter-io.cn" 234 | source: hosted 235 | version: "1.1.6" 236 | vector_math: 237 | dependency: transitive 238 | description: 239 | name: vector_math 240 | url: "https://pub.flutter-io.cn" 241 | source: hosted 242 | version: "2.0.8" 243 | xml: 244 | dependency: transitive 245 | description: 246 | name: xml 247 | url: "https://pub.flutter-io.cn" 248 | source: hosted 249 | version: "3.5.0" 250 | sdks: 251 | dart: ">=2.4.0 <3.0.0" 252 | flutter: ">=1.6.0 <3.0.0" 253 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: xiechengwang_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 | 23 | # The following adds the Cupertino Icons font to your application. 24 | # Use with the CupertinoIcons class for iOS style icons. 25 | cupertino_icons: ^0.1.2 26 | flutter_swiper: ^1.1.4 27 | http: ^0.12.0+4 28 | flutter_webview_plugin: ^0.3.10+1 29 | flutter_staggered_grid_view: ^0.3.0 30 | flutter_splash_screen: ^0.1.0 31 | 32 | dev_dependencies: 33 | flutter_test: 34 | sdk: flutter 35 | 36 | 37 | # For information on the generic Dart part of this file, see the 38 | # following page: https://dart.dev/tools/pub/pubspec 39 | 40 | # The following section is specific to Flutter. 41 | flutter: 42 | 43 | # The following line ensures that the Material Icons font is 44 | # included with your application, so that you can use the icons in 45 | # the material Icons class. 46 | uses-material-design: true 47 | 48 | # To add assets to your application, add an assets section, like this: 49 | assets: 50 | - images/type_channeltrain.png 51 | - images/type_channellgs.png 52 | - images/type_channelplane.png 53 | - images/type_channelgroup.png 54 | - images/type_cruise.png 55 | - images/type_district.png 56 | - images/type_food.png 57 | - images/type_hotel.png 58 | - images/type_huodong.png 59 | - images/type_shop.png 60 | - images/type_sight.png 61 | - images/type_ticket.png 62 | - images/type_travelgroup.png 63 | # - images/a_dot_burr.jpeg 64 | # - images/a_dot_ham.jpeg 65 | 66 | # An image asset can refer to one or more resolution-specific "variants", see 67 | # https://flutter.dev/assets-and-images/#resolution-aware. 68 | 69 | # For details regarding adding assets from package dependencies, see 70 | # https://flutter.dev/assets-and-images/#from-packages 71 | 72 | # To add custom fonts to your application, add a fonts section here, 73 | # in this "flutter" section. Each entry in this list should have a 74 | # "family" key with the font family name, and a "fonts" key with a 75 | # list giving the asset and other descriptors for the font. For 76 | # example: 77 | # fonts: 78 | # - family: Schyler 79 | # fonts: 80 | # - asset: fonts/Schyler-Regular.ttf 81 | # - asset: fonts/Schyler-Italic.ttf 82 | # style: italic 83 | # - family: Trajan Pro 84 | # fonts: 85 | # - asset: fonts/TrajanPro.ttf 86 | # - asset: fonts/TrajanPro_Bold.ttf 87 | # weight: 700 88 | # 89 | # For details regarding fonts from package dependencies, 90 | # see https://flutter.dev/custom-fonts/#from-packages 91 | -------------------------------------------------------------------------------- /screenshot/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/screenshot/1.png -------------------------------------------------------------------------------- /screenshot/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/screenshot/2.png -------------------------------------------------------------------------------- /screenshot/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/screenshot/3.png -------------------------------------------------------------------------------- /screenshot/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/screenshot/4.png -------------------------------------------------------------------------------- /screenshot/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/screenshot/5.png -------------------------------------------------------------------------------- /screenshot/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/screenshot/6.png -------------------------------------------------------------------------------- /screenshot/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/screenshot/7.png -------------------------------------------------------------------------------- /screenshot/alt_1.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/screenshot/alt_1.PNG -------------------------------------------------------------------------------- /screenshot/alt_2.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/screenshot/alt_2.PNG -------------------------------------------------------------------------------- /screenshot/alt_3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/screenshot/alt_3.PNG -------------------------------------------------------------------------------- /screenshot/alt_4.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/screenshot/alt_4.PNG -------------------------------------------------------------------------------- /screenshot/alt_5.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/screenshot/alt_5.PNG -------------------------------------------------------------------------------- /screenshot/alt_6.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/screenshot/alt_6.PNG -------------------------------------------------------------------------------- /screenshot/alt_7.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/screenshot/alt_7.PNG -------------------------------------------------------------------------------- /screenshot/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeOver2015/xiechengwang_app/2bd9351a077456201480db0a9c11e279ec2d5d5a/screenshot/download.png -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:xiechengwang_app/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | --------------------------------------------------------------------------------