├── .gitignore ├── .gradle ├── 5.2.1 │ ├── fileChanges │ │ └── last-build.bin │ ├── fileHashes │ │ └── fileHashes.lock │ └── gc.properties ├── buildOutputCleanup │ ├── buildOutputCleanup.lock │ └── cache.properties └── vcs-1 │ └── gc.properties ├── .metadata ├── README.md ├── android ├── .gitignore ├── app │ ├── build.gradle │ ├── libs │ │ ├── AMap3DMap_7.7.0_AMapSearch_7.7.0_20201027.jar │ │ ├── arm64-v8a │ │ │ └── libAMapSDK_MAP_v7_7_0.so │ │ └── armeabi-v7a │ │ │ └── libAMapSDK_MAP_v7_7_0.so │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── flutter_dynamic_weather │ │ │ │ ├── RainParam.kt │ │ │ │ ├── ThunderParam.kt │ │ │ │ ├── WeatherAnimWidget.kt │ │ │ │ ├── WeatherAnimWidgetService.kt │ │ │ │ ├── WeatherBgFactory.kt │ │ │ │ └── WeatherResFactory.kt │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── flutter_dynamic_weather │ │ │ │ ├── MainActivity.kt │ │ │ │ ├── MarqueeTextView.kt │ │ │ │ ├── base │ │ │ │ ├── ApiManager.kt │ │ │ │ ├── BaseActivity.kt │ │ │ │ ├── BaseCallback.kt │ │ │ │ ├── BaseConstant.kt │ │ │ │ ├── BaseFragment.kt │ │ │ │ ├── BaseFragmentActivity.kt │ │ │ │ ├── CommonUtils.kt │ │ │ │ ├── LogUtils.kt │ │ │ │ ├── ModuleActivity.kt │ │ │ │ ├── UiUtils.kt │ │ │ │ └── WeatherUtils.kt │ │ │ │ ├── fragment │ │ │ │ ├── JikeFragment.kt │ │ │ │ └── ZhuGeFragment.kt │ │ │ │ ├── map │ │ │ │ ├── AmapLocationMarkerView.kt │ │ │ │ ├── MinuteChartView.kt │ │ │ │ ├── MinuteFragment.kt │ │ │ │ ├── MinuteLayout.kt │ │ │ │ ├── MinuteProgressView.kt │ │ │ │ ├── MinuteResponse.kt │ │ │ │ ├── MinuteService.kt │ │ │ │ ├── MinuteViewModel.kt │ │ │ │ ├── RainData.kt │ │ │ │ ├── RealtimeResponse.kt │ │ │ │ └── WeatherAllData.kt │ │ │ │ └── widget │ │ │ │ ├── BigWidgetProvider.kt │ │ │ │ └── BigWidgetSettingActivity.kt │ │ └── res │ │ │ ├── drawable-nodpi │ │ │ └── example_appwidget_preview.png │ │ │ ├── drawable │ │ │ ├── launch_background.xml │ │ │ ├── shape_minute_bottom_bg.xml │ │ │ └── shape_minute_progress_bg.xml │ │ │ ├── layout │ │ │ ├── activity_big_widget_setting.xml │ │ │ ├── fragment_minute.xml │ │ │ ├── layout_jike.xml │ │ │ ├── layout_minute_progress.xml │ │ │ ├── layout_public_activity.xml │ │ │ ├── layout_widget_big.xml │ │ │ ├── layout_zhuge.xml │ │ │ └── weather_anim_widget.xml │ │ │ ├── mipmap-xxhdpi │ │ │ ├── cloud.webp │ │ │ ├── ic_common_location.png │ │ │ ├── ic_common_refresh_vector.png │ │ │ ├── ic_launcher.png │ │ │ ├── ic_rain_area_pause.png │ │ │ ├── ic_rain_area_play.png │ │ │ ├── ic_zoom_in.png │ │ │ ├── ic_zoom_out.png │ │ │ ├── icon_app_widget_tools_4x1_shili.webp │ │ │ ├── icon_app_widget_tools_4x2_shili.webp │ │ │ ├── icon_location.png │ │ │ ├── intensity_of_rainfall_legend.png │ │ │ ├── lightning0.webp │ │ │ ├── lightning1.webp │ │ │ ├── lightning2.webp │ │ │ ├── lightning3.webp │ │ │ ├── lightning4.webp │ │ │ ├── rain.webp │ │ │ └── zx_left_drawer_back.png │ │ │ ├── values-night │ │ │ └── themes.xml │ │ │ ├── values │ │ │ ├── attrs.xml │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ ├── styles.xml │ │ │ └── themes.xml │ │ │ └── xml │ │ │ ├── big_widget_config.xml │ │ │ ├── filepaths.xml │ │ │ ├── network_config.xml │ │ │ └── weather_anim_widget_info.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets └── images │ ├── caiyun.png │ ├── carWashing.png │ ├── coldRisk.png │ ├── comfort.png │ ├── dressing.png │ ├── empty.png │ ├── error.png │ ├── ic_launcher.png │ ├── play.png │ ├── typhoon.png │ ├── ultraviolet.png │ └── weather │ ├── cloudy.png │ ├── cloudy_night.png │ ├── foggy.png │ ├── heavy_rain.png │ ├── heavy_snow.png │ ├── middle_rain.png │ ├── middle_snow.png │ ├── overcast.png │ ├── sandy.png │ ├── small_rain.png │ ├── small_snow.png │ ├── sunny.png │ ├── sunny_night.png │ ├── sunrise.png │ └── sunset.png ├── images ├── check.gif ├── grid.gif ├── home.gif ├── list.gif ├── page.gif ├── qrcode.png ├── weather1.png ├── weather2.png └── weather3.png ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── icon-40.png │ │ ├── icon-40@2x.png │ │ ├── icon-40@3x.png │ │ ├── icon-60@2x.png │ │ ├── icon-60@3x.png │ │ ├── icon-72.png │ │ ├── icon-72@2x.png │ │ ├── icon-76.png │ │ ├── icon-76@2x.png │ │ ├── icon-83.5@2x.png │ │ ├── icon-small-50.png │ │ ├── icon-small-50@2x.png │ │ ├── icon-small.png │ │ ├── icon-small@2x.png │ │ ├── icon-small@3x.png │ │ ├── icon.png │ │ ├── icon@2x.png │ │ ├── ios-marketing.png │ │ ├── notification-icon@2x.png │ │ ├── notification-icon@3x.png │ │ ├── notification-icon~ipad.png │ │ └── notification-icon~ipad@2x.png │ ├── Contents.json │ └── 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 ├── app │ ├── res │ │ ├── analytics_constant.dart │ │ ├── common_extension.dart │ │ ├── dimen_constant.dart │ │ ├── string_constant.dart │ │ ├── weather_type.dart │ │ └── widget_state.dart │ ├── router.dart │ └── utils │ │ ├── color_utils.dart │ │ ├── image_utils.dart │ │ ├── location_util.dart │ │ ├── ota_utils.dart │ │ ├── print_utils.dart │ │ ├── router_utils.dart │ │ ├── shared_preference_util.dart │ │ ├── time_util.dart │ │ ├── toast.dart │ │ └── ui_utils.dart ├── bloc │ ├── city │ │ ├── city_bloc.dart │ │ ├── city_event.dart │ │ └── city_state.dart │ └── weather │ │ ├── weather_bloc.dart │ │ ├── weather_event.dart │ │ └── weather_state.dart ├── event │ └── change_index_envent.dart ├── example │ ├── anim_view.dart │ ├── grid_view.dart │ ├── list_view.dart │ ├── main.dart │ └── page_view.dart ├── generated │ └── json │ │ ├── base │ │ ├── json_convert_content.dart │ │ └── json_field.dart │ │ ├── district_model_entity_helper.dart │ │ └── weather_model_entity_helper.dart ├── main.dart ├── model │ ├── city_data.dart │ ├── city_model_entity.dart │ ├── district_model_entity.dart │ └── weather_model_entity.dart ├── net │ ├── code.dart │ ├── net_manager.dart │ ├── rep_result.dart │ ├── response_interceptor.dart │ └── weather_api.dart └── views │ ├── app │ ├── bloc_wrapper.dart │ └── flutter_app.dart │ ├── bg │ └── weather_main_bg.dart │ ├── common │ ├── blur_rect.dart │ ├── loading_dialog.dart │ ├── loading_view.dart │ ├── ota_dialog.dart │ └── wave_view.dart │ └── pages │ ├── about │ └── about_page.dart │ ├── home │ ├── aqi_chart.dart │ ├── city_view.dart │ ├── day_forecast.dart │ ├── day_forecast_detail.dart │ ├── home_page.dart │ ├── hour_forecast.dart │ ├── life_index.dart │ ├── main_app_bar.dart │ ├── main_message.dart │ ├── rain_detail.dart │ ├── real_time.dart │ ├── real_time_detail.dart │ ├── real_time_temp.dart │ ├── speak_anim.dart │ ├── sun_rise_set.dart │ └── test.dart │ ├── manager │ └── manager_page.dart │ └── search │ ├── hot_city_view.dart │ ├── search_app_bar.dart │ ├── search_list_view.dart │ └── search_page.dart ├── pubspec.lock ├── pubspec.yaml └── 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 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Exceptions to above rules. 44 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 45 | -------------------------------------------------------------------------------- /.gradle/5.2.1/fileChanges/last-build.bin: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gradle/5.2.1/fileHashes/fileHashes.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/.gradle/5.2.1/fileHashes/fileHashes.lock -------------------------------------------------------------------------------- /.gradle/5.2.1/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/.gradle/5.2.1/gc.properties -------------------------------------------------------------------------------- /.gradle/buildOutputCleanup/buildOutputCleanup.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/.gradle/buildOutputCleanup/buildOutputCleanup.lock -------------------------------------------------------------------------------- /.gradle/buildOutputCleanup/cache.properties: -------------------------------------------------------------------------------- 1 | #Wed Sep 16 20:02:20 CST 2020 2 | gradle.version=5.2.1 3 | -------------------------------------------------------------------------------- /.gradle/vcs-1/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/.gradle/vcs-1/gc.properties -------------------------------------------------------------------------------- /.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: bbfbf1770cca2da7c82e887e4e4af910034800b6 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | -------------------------------------------------------------------------------- /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 = '2' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.1' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply plugin: 'kotlin-android-extensions' 27 | apply plugin: 'kotlin-kapt' 28 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 29 | 30 | android { 31 | compileSdkVersion 30 32 | buildToolsVersion "30.0.1" 33 | 34 | sourceSets { 35 | main.java.srcDirs += 'src/main/kotlin' 36 | main { 37 | jniLibs.srcDirs = ['libs'] 38 | } 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.example.flutter_dynamic_weather" 48 | minSdkVersion 21 49 | targetSdkVersion 30 50 | versionCode flutterVersionCode.toInteger() 51 | versionName flutterVersionName 52 | } 53 | 54 | buildTypes { 55 | release { 56 | // TODO: Add your own signing config for the release build. 57 | // Signing with the debug keys for now, so `flutter run --release` works. 58 | signingConfig signingConfigs.debug 59 | } 60 | } 61 | } 62 | 63 | flutter { 64 | source '../..' 65 | } 66 | 67 | dependencies { 68 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 69 | implementation 'androidx.core:core-ktx:1.3.2' 70 | implementation 'androidx.appcompat:appcompat:1.2.0' 71 | implementation 'androidx.constraintlayout:constraintlayout:2.0.2' 72 | testImplementation 'junit:junit:4.12' 73 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 74 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 75 | implementation files('libs/AMap3DMap_7.7.0_AMapSearch_7.7.0_20201027.jar') 76 | // Coroutines 77 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.7' 78 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2' 79 | // retrofit 80 | implementation "com.squareup.retrofit2:retrofit:$retrofit_version" 81 | implementation "com.squareup.retrofit2:converter-gson:$retrofit_version" 82 | // ViewModel 83 | implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" 84 | // LiveData 85 | implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" 86 | // Lifecycles only (without ViewModel or LiveData) 87 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" 88 | implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2' 89 | implementation 'com.squareup.okhttp3:logging-interceptor:4.9.0' 90 | implementation "com.github.bumptech.glide:glide:$glideVersion" 91 | } 92 | -------------------------------------------------------------------------------- /android/app/libs/AMap3DMap_7.7.0_AMapSearch_7.7.0_20201027.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/libs/AMap3DMap_7.7.0_AMapSearch_7.7.0_20201027.jar -------------------------------------------------------------------------------- /android/app/libs/arm64-v8a/libAMapSDK_MAP_v7_7_0.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/libs/arm64-v8a/libAMapSDK_MAP_v7_7_0.so -------------------------------------------------------------------------------- /android/app/libs/armeabi-v7a/libAMapSDK_MAP_v7_7_0.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/libs/armeabi-v7a/libAMapSDK_MAP_v7_7_0.so -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/example/flutter_dynamic_weather/RainParam.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather 2 | 3 | import android.graphics.Bitmap 4 | import android.graphics.Point 5 | import java.util.* 6 | 7 | /** 8 | *
 9 |  *     class  : com.example.flutter_dynamic_weather.RainParam
10 |  *     time   : 2021/03/10
11 |  *     desc   :
12 |  * 
13 | */ 14 | data class RainParam( 15 | var x: Float = 0f, 16 | var y: Float = 0f, 17 | var scale: Float = 1f, 18 | var alpha: Float = 1f, 19 | var speed: Float = 10.5f, 20 | var bitmap: Bitmap = WeatherResFactory.instance.rainBitmap!!, 21 | var point: Point, 22 | ) { 23 | 24 | init { 25 | scale = Random().nextFloat() * 0.5f + 0.5f 26 | x = (point.x + 2 * bitmap.width) * Random().nextFloat() - bitmap.width 27 | y = (point.y + 2 * bitmap.height) * Random().nextFloat() - bitmap.height 28 | x /= scale 29 | y /= scale 30 | alpha *= scale * 0.2f + 0.8f 31 | } 32 | 33 | private fun reset() { 34 | scale = Random().nextFloat() * 0.1f + 0.9f 35 | x = (point.x + 2 * bitmap.width) * Random().nextFloat() - bitmap.width 36 | y = (-bitmap.height).toFloat() 37 | x /= scale 38 | alpha = scale * 0.8f + 0.2f 39 | } 40 | 41 | fun move(point: Point = this.point) { 42 | y += speed 43 | if (y > point.y + bitmap.height) { 44 | reset() 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/example/flutter_dynamic_weather/ThunderParam.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather 2 | 3 | import android.graphics.Bitmap 4 | import android.graphics.Point 5 | import android.view.animation.AccelerateInterpolator 6 | import android.view.animation.DecelerateInterpolator 7 | import java.util.* 8 | import kotlin.math.abs 9 | 10 | /** 11 | *
12 |  *     class  : com.example.flutter_dynamic_weather.RainParam
13 |  *     time   : 2021/03/10
14 |  *     desc   :
15 |  * 
16 | */ 17 | data class ThunderParam( 18 | var x: Float = 0f, 19 | var y: Float = 0f, 20 | var alpha: Float = 1f, 21 | var progress: Float = 0f, // 0-1 show 9-10 hide 22 | var show: Boolean = true, 23 | var bitmap: Bitmap = WeatherResFactory.instance.rainBitmap!!, 24 | var point: Point 25 | ) { 26 | 27 | init { 28 | val index = (Random().nextFloat() * 4).toInt() 29 | bitmap = WeatherResFactory.instance.thunderBitmap[index]!! 30 | x = (point.x / 2f + bitmap.width / 2f) * Random().nextFloat() - bitmap.width / 2f 31 | y = 1f / 5 * bitmap.height * Random().nextFloat() - bitmap.height * 1f / 5 32 | progress = 12f * Random().nextFloat() 33 | alpha = when { 34 | abs(progress) in 0f..1.0f -> { 35 | progress 36 | } 37 | abs(progress) in 1.0f..2.0f -> { 38 | 2.0f - progress 39 | } 40 | else -> { 41 | 0f 42 | } 43 | } 44 | } 45 | 46 | 47 | fun move() { 48 | progress += 0.1f 49 | if (progress >= 12f) { 50 | progress = 0.0f 51 | } 52 | alpha = when { 53 | abs(progress) in 0f..1.0f -> { 54 | AccelerateInterpolator().getInterpolation(progress) 55 | } 56 | abs(progress) in 1.0f..2.0f -> { 57 | DecelerateInterpolator().getInterpolation(2.0f - progress) 58 | } 59 | else -> { 60 | 0f 61 | } 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/example/flutter_dynamic_weather/WeatherAnimWidget.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather 2 | 3 | import android.appwidget.AppWidgetManager 4 | import android.appwidget.AppWidgetProvider 5 | import android.content.Context 6 | import android.graphics.Bitmap 7 | import android.util.Log 8 | import android.widget.RemoteViews 9 | 10 | /** 11 | * Implementation of App Widget functionality. 12 | */ 13 | class WeatherAnimWidget : AppWidgetProvider() { 14 | 15 | companion object { 16 | private const val TAG = "WeatherService-widget::" 17 | internal fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int, bitmap: Bitmap? = null) { 18 | val views = RemoteViews(context.packageName, R.layout.weather_anim_widget) 19 | if (bitmap != null) { 20 | views.setImageViewBitmap(R.id.appwidget_bg, bitmap) 21 | } 22 | appWidgetManager.updateAppWidget(appWidgetId, views) 23 | } 24 | } 25 | 26 | override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) { 27 | for (appWidgetId in appWidgetIds) { 28 | updateAppWidget(context, appWidgetManager, appWidgetId) 29 | } 30 | } 31 | 32 | override fun onEnabled(context: Context) { 33 | WeatherAnimWidgetService.startService(context) 34 | } 35 | 36 | override fun onDisabled(context: Context) { 37 | 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/example/flutter_dynamic_weather/WeatherBgFactory.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather 2 | 3 | import android.graphics.Bitmap 4 | 5 | /** 6 | *
 7 |  *     class  : com.example.flutter_dynamic_weather.WeatherBgFactory
 8 |  *     time   : 2021/03/10
 9 |  *     desc   :
10 |  * 
11 | */ 12 | interface WeatherBgFactory { 13 | fun crate(): Bitmap 14 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/example/flutter_dynamic_weather/WeatherResFactory.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather 2 | 3 | import android.content.Context 4 | import android.graphics.Bitmap 5 | import android.graphics.BitmapFactory 6 | import android.util.Log 7 | import com.example.flutter_dynamic_weather.base.ApiManager 8 | 9 | /** 10 | *
11 |  *     class  : com.example.flutter_dynamic_weather.WeatherResFactory
12 |  *     time   : 2021/03/10
13 |  *     desc   : 资源工厂
14 |  * 
15 | */ 16 | class WeatherResFactory private constructor() { 17 | 18 | var rainBitmap: Bitmap? = null 19 | var thunderBitmap = arrayListOf() 20 | var cloudBitmap: Bitmap? = null 21 | var isPrepare = false 22 | 23 | companion object { 24 | private const val TAG = "Weather-Res:" 25 | val instance by lazy { 26 | WeatherResFactory() 27 | } 28 | } 29 | 30 | fun prepareRes(context: Context) { 31 | val lastTime = System.currentTimeMillis() 32 | rainBitmap = BitmapFactory.decodeResource(context.resources, R.mipmap.rain) 33 | cloudBitmap = BitmapFactory.decodeResource(context.resources, R.mipmap.cloud) 34 | thunderBitmap.clear() 35 | thunderBitmap.add(BitmapFactory.decodeResource(context.resources, R.mipmap.lightning0)) 36 | thunderBitmap.add(BitmapFactory.decodeResource(context.resources, R.mipmap.lightning1)) 37 | thunderBitmap.add(BitmapFactory.decodeResource(context.resources, R.mipmap.lightning2)) 38 | thunderBitmap.add(BitmapFactory.decodeResource(context.resources, R.mipmap.lightning3)) 39 | thunderBitmap.add(BitmapFactory.decodeResource(context.resources, R.mipmap.lightning4)) 40 | Log.d(TAG, "prepareRes: total time: ${System.currentTimeMillis() - lastTime}") 41 | isPrepare = true 42 | } 43 | 44 | fun release() { 45 | rainBitmap?.recycle() 46 | rainBitmap = null 47 | } 48 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather 2 | 3 | import android.os.Bundle 4 | import android.util.Log 5 | import com.example.flutter_dynamic_weather.base.CommonUtils 6 | import com.example.flutter_dynamic_weather.fragment.JikeFragment 7 | import com.example.flutter_dynamic_weather.fragment.ZhuGeFragment 8 | import io.flutter.embedding.android.FlutterActivity 9 | import io.flutter.plugin.common.MethodCall 10 | import io.flutter.plugin.common.MethodChannel 11 | 12 | class MainActivity : FlutterActivity() { 13 | 14 | companion object { 15 | private const val CHANNEL_NAME = "com.example.flutter_dynamic_weather/router" 16 | private const val START_ACTIVITY = "startActivity" 17 | private const val ACTIVITY_NAME = "minute" 18 | private const val ACTIVITY_ZHUGE = "zhuge" 19 | private const val ACTIVITY_JIKE = "jike" 20 | } 21 | 22 | override fun onCreate(savedInstanceState: Bundle?) { 23 | super.onCreate(savedInstanceState) 24 | WeatherAnimWidgetService.startService(this) 25 | val nativeChannel = MethodChannel(flutterEngine?.dartExecutor, CHANNEL_NAME) 26 | 27 | nativeChannel.setMethodCallHandler { call, result -> 28 | when (call.method) { 29 | START_ACTIVITY -> { 30 | val name = call.argument("name") 31 | if (name == ACTIVITY_NAME) { 32 | // 跳转到降雨二级页 33 | CommonUtils.startMinutePage(this) 34 | } else if (name == ACTIVITY_ZHUGE) { 35 | CommonUtils.startModuleActivity(this, ZhuGeFragment::class.java.name) 36 | } else if (name == ACTIVITY_JIKE) { 37 | CommonUtils.startModuleActivity(this, JikeFragment::class.java.name) 38 | } 39 | } 40 | } 41 | } 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/MarqueeTextView.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.widget.TextView 6 | 7 | /** 8 | *
 9 |  *     author : xiaweizi
10 |  *     class  : com.eiffelyk.weather.weizi.middle.view.MarqueeTextView
11 |  *     e-mail : 1012126908@qq.com
12 |  *     time   : 2020/11/02
13 |  *     desc   :
14 |  * 
15 | */ 16 | class MarqueeTextView : androidx.appcompat.widget.AppCompatTextView { 17 | constructor(context: Context) : super(context) 18 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) 19 | constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super( 20 | context, 21 | attrs, 22 | defStyleAttr 23 | ) 24 | 25 | override fun isFocused(): Boolean { 26 | return true 27 | } 28 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/base/ApiManager.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.base 2 | 3 | import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory 4 | import okhttp3.OkHttpClient 5 | import okhttp3.logging.HttpLoggingInterceptor 6 | import retrofit2.Retrofit 7 | import retrofit2.converter.gson.GsonConverterFactory 8 | 9 | 10 | /** 11 | *
12 |  *     author : xiaweizi
13 |  *     class  : com.eiffelyk.weather.weizi.main.net.ApiManager
14 |  *     e-mail : 1012126908@qq.com
15 |  *     time   : 2020/10/16
16 |  *     desc   :
17 |  * 
18 | */ 19 | class ApiManager private constructor() { 20 | private val retrofit: Retrofit = Retrofit.Builder().apply { 21 | val logging = HttpLoggingInterceptor() 22 | logging.setLevel(HttpLoggingInterceptor.Level.BODY) 23 | val httpClient = OkHttpClient.Builder() 24 | httpClient.addInterceptor(logging) 25 | client(httpClient.build()) 26 | baseUrl(BaseConstant.BASE_URL) 27 | addConverterFactory(GsonConverterFactory.create()) 28 | addCallAdapterFactory(CoroutineCallAdapterFactory.invoke()) 29 | }.build() 30 | 31 | fun createService(clazz: Class): T { 32 | return retrofit.create(clazz) 33 | } 34 | 35 | companion object { 36 | val instance by lazy { 37 | ApiManager() 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.base 2 | 3 | import android.content.Context 4 | import android.content.res.Configuration 5 | import android.os.Build 6 | import android.os.Bundle 7 | import android.view.Gravity 8 | import android.view.View 9 | import android.widget.Toast 10 | import androidx.appcompat.app.AppCompatActivity 11 | 12 | /** 13 | *
14 |  *     author : xiaweizi
15 |  *     class  : com.example.flutter_dynamic_weather.base.BaseActivity
16 |  *     e-mail : 1012126908@qq.com
17 |  *     time   : 2020/10/15
18 |  *     desc   :
19 |  * 
20 | */ 21 | abstract class BaseActivity : AppCompatActivity(), BaseCallback { 22 | 23 | override fun onCreate(savedInstanceState: Bundle?) { 24 | super.onCreate(savedInstanceState) 25 | setContentView(getLayoutId()) 26 | initView() 27 | initData() 28 | } 29 | 30 | /** 31 | * 是否可以使用沉浸式 32 | * Is immersion bar enabled boolean. 33 | * 34 | * @return the boolean 35 | */ 36 | protected open fun isImmersionBarEnabled(): Boolean { 37 | return true 38 | } 39 | 40 | 41 | override fun onConfigurationChanged(newConfig: Configuration) { 42 | super.onConfigurationChanged(newConfig) 43 | 44 | } 45 | 46 | override fun initView() { 47 | 48 | } 49 | 50 | override fun initData() { 51 | 52 | } 53 | 54 | private var toast: Toast? = null 55 | 56 | open fun showToastCenter(context: Context?, msg: String?) { 57 | if (toast != null) { 58 | toast!!.cancel() 59 | toast = null 60 | } 61 | toast = Toast.makeText(context, "", Toast.LENGTH_SHORT) //如果有居中显示需求 62 | toast?.setGravity(Gravity.CENTER, 0, 0) 63 | toast?.setText(msg) 64 | toast?.show() 65 | } 66 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/base/BaseCallback.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.base 2 | 3 | import androidx.annotation.LayoutRes 4 | 5 | /** 6 | *
 7 |  *     author : xiaweizi
 8 |  *     class  : com.example.flutter_dynamic_weather.interfaces.BaseCallback
 9 |  *     e-mail : 1012126908@qq.com
10 |  *     time   : 2020/10/15
11 |  *     desc   :
12 |  * 
13 | */ 14 | interface BaseCallback { 15 | @LayoutRes 16 | fun getLayoutId(): Int 17 | fun initView() 18 | fun initData() 19 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/base/BaseConstant.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.base 2 | 3 | /** 4 | *
 5 |  *     author : xiaweizi
 6 |  *     class  : com.example.flutter_dynamic_weather.base.BaseConstant
 7 |  *     e-mail : 1012126908@qq.com
 8 |  *     time   : 2020/10/16
 9 |  *     desc   :
10 |  * 
11 | */ 12 | class BaseConstant { 13 | companion object { 14 | const val BASE_URL = "https://api.caiyunapp.com/" 15 | const val FRAGMENT_NAME = "fragmentName" 16 | } 17 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/base/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.base 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import android.text.TextUtils 6 | import android.view.KeyEvent 7 | import android.view.LayoutInflater 8 | import android.view.View 9 | import android.view.ViewGroup 10 | import android.widget.Toast 11 | import androidx.fragment.app.Fragment 12 | import androidx.lifecycle.ViewModelProvider 13 | import com.umeng.analytics.MobclickAgent 14 | 15 | 16 | /** 17 | *
18 |  *     author : xiaweizi
19 |  *     class  : com.example.flutter_dynamic_weather.base.BaseFragment
20 |  *     e-mail : 1012126908@qq.com
21 |  *     time   : 2020/10/15
22 |  *     desc   :
23 |  * 
24 | */ 25 | abstract class BaseFragment : Fragment(), BaseCallback { 26 | 27 | private lateinit var mRootView: View 28 | 29 | override fun onCreateView( 30 | inflater: LayoutInflater, 31 | container: ViewGroup?, 32 | savedInstanceState: Bundle? 33 | ): View? { 34 | mRootView = inflater.inflate(getLayoutId(), container, false) 35 | return mRootView 36 | } 37 | 38 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 39 | super.onViewCreated(view, savedInstanceState) 40 | initView() 41 | initData() 42 | } 43 | 44 | 45 | override fun initView() { 46 | } 47 | 48 | override fun initData() { 49 | } 50 | 51 | fun finish() { 52 | if (activity != null && !activity!!.isFinishing) { 53 | activity?.finish() 54 | } 55 | } 56 | 57 | open fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { 58 | return false 59 | } 60 | 61 | open fun onBackPressed(): Boolean { 62 | return false 63 | } 64 | 65 | open fun onNewIntent(intent: Intent?) { 66 | } 67 | 68 | // Fragment页面onResume函数重载 69 | override fun onResume() { 70 | super.onResume() 71 | MobclickAgent.onPageStart(this.javaClass.simpleName) 72 | } 73 | 74 | // Fragment页面onResume函数重载 75 | override fun onPause() { 76 | super.onPause() 77 | MobclickAgent.onPageEnd(this.javaClass.simpleName) 78 | } 79 | 80 | protected open fun showToast(message: String?) { 81 | if (!TextUtils.isEmpty(message)) { 82 | Toast.makeText(context, message, Toast.LENGTH_SHORT).show() 83 | } 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/base/BaseFragmentActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.base 2 | 3 | import android.content.Intent 4 | import android.view.KeyEvent 5 | import com.example.flutter_dynamic_weather.R 6 | 7 | /** 8 | *
 9 |  *     author : xiaweizi
10 |  *     class  : com.example.flutter_dynamic_weather.base.BaseFragmentActivity
11 |  *     e-mail : 1012126908@qq.com
12 |  *     time   : 2020/10/15
13 |  *     desc   :
14 |  * 
15 | */ 16 | abstract class BaseFragmentActivity : BaseActivity() { 17 | var mFragment: BaseFragment? = null 18 | override fun getLayoutId() = R.layout.layout_public_activity 19 | override fun initView() { 20 | super.initView() 21 | // 初始化 fragment 22 | mFragment = getFragment() 23 | if (mFragment != null) { 24 | val beginTransaction = supportFragmentManager.beginTransaction() 25 | beginTransaction.replace(R.id.fl_root, mFragment!!) 26 | beginTransaction.commitAllowingStateLoss() 27 | } 28 | } 29 | 30 | abstract fun getFragment(): BaseFragment? 31 | 32 | override fun onNewIntent(intent: Intent?) { 33 | super.onNewIntent(intent) 34 | mFragment?.onNewIntent(intent) 35 | } 36 | 37 | override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { 38 | if (mFragment != null) { 39 | if (!mFragment!!.onKeyDown(keyCode, event)) { 40 | return super.onKeyDown(keyCode, event) 41 | } 42 | } 43 | return super.onKeyDown(keyCode, event) 44 | } 45 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/base/CommonUtils.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.base 2 | 3 | import android.app.ActivityManager 4 | import android.app.PendingIntent 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.location.LocationManager 8 | import android.net.Uri 9 | import androidx.core.content.ContextCompat.startActivity 10 | import com.eiffelyk.weather.weizi.map.MinuteFragment 11 | 12 | 13 | /** 14 | *
15 |  *     author : xiaweizi
16 |  *     class  : com.eiffelyk.weather.weizi.middle.util.CommonUtils
17 |  *     e-mail : 1012126908@qq.com
18 |  *     time   : 2020/10/21
19 |  *     desc   :
20 |  * 
21 | */ 22 | 23 | fun Intent.putParams(params: HashMap) { 24 | params.forEach { 25 | this.putExtra(it.key, it.value) 26 | } 27 | } 28 | 29 | class CommonUtils { 30 | companion object { 31 | private const val TAG = "CommonUtils::" 32 | 33 | fun startModuleActivity( 34 | context: Context, 35 | fragmentName: String, 36 | handleIntent: ((Intent) -> Unit)? = null 37 | ) { 38 | LogUtils.i(TAG, "startModuleActivity: $fragmentName") 39 | try { 40 | val intent = Intent(context, ModuleActivity::class.java) 41 | intent.putExtra(BaseConstant.FRAGMENT_NAME, fragmentName) 42 | handleIntent?.invoke(intent) 43 | startActivity(context, intent, null) 44 | } catch (e: Exception) { 45 | LogUtils.i(TAG, "startModuleActivity error ${e.message}") 46 | } 47 | } 48 | 49 | fun startMinutePage(context: Context, handleIntent: ((Intent) -> Unit)? = null) { 50 | startModuleActivity(context, MinuteFragment::class.java.name, handleIntent) 51 | } 52 | 53 | } 54 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/base/LogUtils.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.base 2 | 3 | import android.util.Log 4 | 5 | /** 6 | *
 7 |  *     author : xiaweizi
 8 |  *     class  : com.eiffelyk.weather.weizi.middle.util.LogUtils
 9 |  *     e-mail : 1012126908@qq.com
10 |  *     time   : 2020/10/19
11 |  *     desc   : 日志工具类
12 |  * 
13 | */ 14 | class LogUtils { 15 | companion object { 16 | fun i(tag: String, content: String) { 17 | Log.i("Weather-$tag", content) 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/base/ModuleActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.base 2 | 3 | /** 4 | *
 5 |  *     author : xiaweizi
 6 |  *     class  : com.example.flutter_dynamic_weather.base.ModuleActivity
 7 |  *     e-mail : 1012126908@qq.com
 8 |  *     time   : 2020/10/21
 9 |  *     desc   :
10 |  * 
11 | */ 12 | class ModuleActivity: BaseFragmentActivity() { 13 | 14 | override fun getFragment(): BaseFragment? { 15 | return newInsFragment() 16 | } 17 | 18 | private fun newInsFragment(): BaseFragment? { 19 | if (mFragment == null) { 20 | try { 21 | val fragmentName = intent.getStringExtra(BaseConstant.FRAGMENT_NAME) 22 | val onwClass = Class.forName(fragmentName!!) 23 | val instance = onwClass.newInstance() 24 | mFragment = instance as BaseFragment 25 | } catch (e: Throwable) { 26 | e.printStackTrace() 27 | } 28 | } 29 | return mFragment 30 | } 31 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/base/UiUtils.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.base 2 | 3 | import android.content.res.Resources 4 | import android.util.DisplayMetrics 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.fragment.app.FragmentActivity 7 | 8 | /** 9 | *
10 |  *     author : xiaweizi
11 |  *     class  : com.eiffelyk.weather.weizi.middle.util.UiUtils
12 |  *     e-mail : 1012126908@qq.com
13 |  *     time   : 2020/10/17
14 |  *     desc   :
15 |  * 
16 | */ 17 | class UiUtils { 18 | companion object { 19 | fun getStatusBarHeight(): Int { 20 | val resources = Resources.getSystem() 21 | val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android") 22 | return resources.getDimensionPixelOffset(resourceId) 23 | } 24 | 25 | fun getScreenHeight(context: FragmentActivity): Int { 26 | val outMetrics = DisplayMetrics() 27 | context.windowManager.defaultDisplay.getMetrics(outMetrics) 28 | return outMetrics.heightPixels 29 | } 30 | 31 | fun getScreenWidth(context: FragmentActivity): Int { 32 | val outMetrics = DisplayMetrics() 33 | context.windowManager.defaultDisplay.getRealMetrics(outMetrics) 34 | return outMetrics.widthPixels 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/base/WeatherUtils.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.base 2 | 3 | /** 4 | *
 5 |  *     author ,xiaweizi
 6 |  *     class  ,com.example.flutter_dynamic_weather.base.WeatherUtils
 7 |  *     e-mail ,1012126908@qq.com
 8 |  *     time   ,2020/11/15
 9 |  *     desc   :
10 |  * 
11 | */ 12 | class WeatherUtils { 13 | companion object { 14 | val weatherMap = hashMapOf( 15 | Pair("CLEAR_DAY", "晴"), 16 | Pair("CLEAR_NIGHT", "晴"), 17 | Pair("PARTLY_CLOUDY_DAY", "多云"), 18 | Pair("PARTLY_CLOUDY_NIGHT", "多云"), 19 | Pair("CLOUDY", "阴"), 20 | Pair("LIGHT_HAZE", "霾"), 21 | Pair("MODERATE_HAZE", "霾"), 22 | Pair("HEAVY_HAZE", "霾"), 23 | Pair("LIGHT_RAIN", "小雨"), 24 | Pair("MODERATE_RAIN", "中雨"), 25 | Pair("HEAVY_RAIN", "大雨"), 26 | Pair("STORM_RAIN", "暴雨"), 27 | Pair("FOG", "雾"), 28 | Pair("LIGHT_SNOW", "小雪"), 29 | Pair("MODERATE_SNOW", "中雪"), 30 | Pair("HEAVY_SNOW", "大雪"), 31 | Pair("STORM_SNOW", "暴雪"), 32 | Pair("DUST", "浮尘"), 33 | Pair("SAND", "沙尘"), 34 | Pair("WIND", "大风"), 35 | ) 36 | 37 | fun getWeatherDesc(type: String): String? { 38 | return weatherMap[type] 39 | } 40 | 41 | fun getAqiDesc(aqi: Int): String? { 42 | if (aqi in 0..50) { 43 | return "优" 44 | } 45 | if (aqi in 51..100) { 46 | return "良" 47 | } 48 | if (aqi in 101..150) { 49 | return "轻度污染" 50 | } 51 | if (aqi in 151..200) { 52 | return "中度污染" 53 | } 54 | if (aqi in 201..300) { 55 | return "重度污染" 56 | } 57 | return if (aqi > 300) { 58 | "严重污染" 59 | } else "" 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/fragment/JikeFragment.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.fragment 2 | 3 | import com.example.flutter_dynamic_weather.R 4 | import com.example.flutter_dynamic_weather.base.BaseFragment 5 | 6 | /** 7 | *
 8 |  *     author : xiaweizi
 9 |  *     class  : com.example.flutter_dynamic_weather.fragment.ZhuGeFragment
10 |  *     e-mail : 1012126908@qq.com
11 |  *     time   : 2020/11/22
12 |  *     desc   :
13 |  * 
14 | */ 15 | class JikeFragment : BaseFragment() { 16 | override fun getLayoutId() = R.layout.layout_jike 17 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/fragment/ZhuGeFragment.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.fragment 2 | 3 | import com.example.flutter_dynamic_weather.R 4 | import com.example.flutter_dynamic_weather.base.BaseFragment 5 | 6 | /** 7 | *
 8 |  *     author : xiaweizi
 9 |  *     class  : com.example.flutter_dynamic_weather.fragment.ZhuGeFragment
10 |  *     e-mail : 1012126908@qq.com
11 |  *     time   : 2020/11/22
12 |  *     desc   :
13 |  * 
14 | */ 15 | class ZhuGeFragment : BaseFragment() { 16 | override fun getLayoutId() = R.layout.layout_zhuge 17 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/map/AmapLocationMarkerView.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.map 2 | 3 | import android.content.Context 4 | import android.graphics.Canvas 5 | import android.graphics.Paint 6 | import android.util.AttributeSet 7 | import android.view.View 8 | import com.example.flutter_dynamic_weather.R 9 | 10 | /** 11 | *
12 |  *     author : xiaweizi
13 |  *     class  : com.eiffelyk.weather.weizi.map.AmapLocationMarkerView
14 |  *     e-mail : 1012126908@qq.com
15 |  *     time   : 2020/11/08
16 |  *     desc   :
17 |  * 
18 | */ 19 | class AmapLocationMarkerView : View { 20 | private var mOuterRadius = 0 21 | private var mInnerRadius = 0 22 | private var mPaint: Paint? = null 23 | private var mOuterColor = 0 24 | private var mInnerColor = 0 25 | 26 | constructor(context: Context?) : this(context, null) 27 | constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, -1) 28 | constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super( 29 | context, 30 | attrs, 31 | defStyleAttr 32 | ) { 33 | initView(context!!) 34 | } 35 | 36 | 37 | private fun initView(context: Context) { 38 | mOuterRadius = 39 | resources.getDimensionPixelSize(R.dimen.amap_marker_view_located_outer_radius) 40 | mInnerRadius = 41 | resources.getDimensionPixelSize(R.dimen.amap_marker_view_located_inner_radius) 42 | mOuterColor = resources.getColor(R.color.amap_marker_view_located_outer_color) 43 | mInnerColor = resources.getColor(R.color.amap_marker_view_located_inner_color) 44 | mPaint = Paint(Paint.ANTI_ALIAS_FLAG) 45 | mPaint!!.style = Paint.Style.FILL 46 | } 47 | 48 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { 49 | super.onMeasure(widthMeasureSpec, heightMeasureSpec) 50 | setMeasuredDimension(mOuterRadius * 2, mOuterRadius * 2) 51 | } 52 | 53 | override fun onDraw(canvas: Canvas) { 54 | super.onDraw(canvas) 55 | mPaint!!.color = mOuterColor 56 | canvas.drawCircle( 57 | width / 2.toFloat(), height / 2.toFloat(), mOuterRadius.toFloat(), 58 | mPaint!! 59 | ) 60 | mPaint!!.color = mInnerColor 61 | canvas.drawCircle( 62 | width / 2.toFloat(), height / 2.toFloat(), mInnerRadius.toFloat(), 63 | mPaint!! 64 | ) 65 | } 66 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/map/MinuteProgressView.kt: -------------------------------------------------------------------------------- 1 | package com.eiffelyk.weather.weizi.map 2 | 3 | import android.annotation.SuppressLint 4 | import android.content.Context 5 | import android.graphics.* 6 | import android.util.AttributeSet 7 | import android.view.MotionEvent 8 | import android.view.View 9 | import com.example.flutter_dynamic_weather.R 10 | import kotlin.math.max 11 | import kotlin.math.min 12 | 13 | /** 14 | *
 15 |  *     author : xiaweizi
 16 |  *     class  : com.eiffelyk.weather.weizi.map.MinuteProgressView
 17 |  *     e-mail : 1012126908@qq.com
 18 |  *     time   : 2020/11/08
 19 |  *     desc   :
 20 |  * 
21 | */ 22 | class MinuteProgressView : View { 23 | private val mStartColor by lazy { 24 | resources.getColor(R.color.amap_progress_bg_start_color) 25 | } 26 | private val mEndColor by lazy { 27 | resources.getColor(R.color.amap_progress_bg_end_color) 28 | } 29 | private val mPaint = Paint(Paint.ANTI_ALIAS_FLAG) 30 | private val mRectF = RectF() 31 | 32 | private var mLinearGradient: LinearGradient? = null 33 | private var mRatio = 0.0f 34 | private var mCallback: OnProgressCallback? = null 35 | private var mIsPlaying = false 36 | 37 | constructor(context: Context?) : this(context, null) 38 | constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, -1) 39 | constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super( 40 | context, 41 | attrs, 42 | defStyleAttr 43 | ) 44 | 45 | fun setProgressCallback(callback: OnProgressCallback?) { 46 | this.mCallback = callback 47 | } 48 | 49 | fun setRatio(ratio: Float) { 50 | mRatio = ratio 51 | invalidate() 52 | } 53 | 54 | override fun onDraw(canvas: Canvas?) { 55 | super.onDraw(canvas) 56 | if (mLinearGradient == null) { 57 | mLinearGradient = LinearGradient( 58 | 0f, 59 | 0f, 60 | width.toFloat(), 61 | height.toFloat(), 62 | intArrayOf(mStartColor, mEndColor), 63 | null, 64 | Shader.TileMode.CLAMP 65 | ) 66 | mPaint.shader = mLinearGradient 67 | } 68 | mRectF.set(0f, 0f, width.toFloat() * mRatio, height.toFloat()) 69 | canvas?.drawRect(mRectF, mPaint) 70 | } 71 | 72 | @SuppressLint("ClickableViewAccessibility") 73 | override fun onTouchEvent(event: MotionEvent?): Boolean { 74 | if (event != null && mCallback != null) { 75 | val x = event.x 76 | when (event.actionMasked) { 77 | MotionEvent.ACTION_DOWN -> { 78 | mRatio = min(width.toFloat(), max(0f, x)) / width 79 | invalidate() 80 | mIsPlaying = mCallback?.onDown(mRatio) ?: false 81 | } 82 | MotionEvent.ACTION_MOVE -> { 83 | mRatio = min(width.toFloat(), max(0f, x)) / width 84 | invalidate() 85 | mCallback?.onMove(mRatio) 86 | } 87 | MotionEvent.ACTION_UP -> { 88 | mCallback?.onUp(mIsPlaying) 89 | } 90 | 91 | } 92 | } 93 | return true 94 | } 95 | 96 | interface OnProgressCallback { 97 | fun onDown(ratio: Float): Boolean 98 | fun onMove(ratio: Float) 99 | fun onUp(isPlaying: Boolean) 100 | } 101 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/map/MinuteResponse.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.map 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | 6 | /** 7 | *
 8 |  *     author : xiaweizi
 9 |  *     class  : com.example.flutter_dynamic_weather.map.MinuteResponse
10 |  *     e-mail : 1012126908@qq.com
11 |  *     time   : 2020/11/15
12 |  *     desc   :
13 |  * 
14 | */ 15 | data class MinuteResponse( 16 | @SerializedName("api_status") 17 | val apiStatus: String, // active 18 | @SerializedName("api_version") 19 | val apiVersion: String, // v2.5 20 | @SerializedName("lang") 21 | val lang: String, // zh_CN 22 | @SerializedName("location") 23 | val location: List, 24 | @SerializedName("result") 25 | val result: Result, 26 | @SerializedName("server_time") 27 | val serverTime: Int, // 1605416697 28 | @SerializedName("status") 29 | val status: String, // ok 30 | @SerializedName("timezone") 31 | val timezone: String, // Asia/Taipei 32 | @SerializedName("tzshift") 33 | val tzshift: Int, // 28800 34 | @SerializedName("unit") 35 | val unit: String // metric 36 | ) 37 | 38 | data class Result( 39 | @SerializedName("forecast_keypoint") 40 | val forecastKeypoint: String, // 大风起兮……注意不要被风刮跑 41 | @SerializedName("minutely") 42 | val minutely: Minutely, 43 | @SerializedName("primary") 44 | val primary: Int // 0 45 | ) 46 | 47 | data class Minutely( 48 | @SerializedName("datasource") 49 | val datasource: String, // gfs 50 | @SerializedName("description") 51 | val description: String, // 大风起兮……注意不要被风刮跑 52 | @SerializedName("precipitation") 53 | val precipitation: List, 54 | @SerializedName("precipitation_2h") 55 | val precipitation2h: List, 56 | @SerializedName("probability") 57 | val probability: List, 58 | @SerializedName("status") 59 | val status: String // ok 60 | ) -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/map/MinuteService.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.map 2 | 3 | import okhttp3.ResponseBody 4 | import retrofit2.Call 5 | import retrofit2.http.GET 6 | import retrofit2.http.Path 7 | import retrofit2.http.Query 8 | 9 | /** 10 | *
11 |  *     author : xiaweizi
12 |  *     class  : com.example.flutter_dynamic_weather.map.MinuteService
13 |  *     e-mail : 1012126908@qq.com
14 |  *     time   : 2020/11/08
15 |  *     desc   :
16 |  * 
17 | */ 18 | interface MinuteService { 19 | 20 | @GET("/v1/radar/forecast_images?level=2&token=leWWGdduHOh6bAkw") 21 | fun getForecastImages(@Query("lon") lon: String, @Query("lat") lat: String): Call 22 | 23 | @GET("/v2.5/leWWGdduHOh6bAkw/{lon},{lat}/minutely.json") 24 | fun getMinutely(@Path("lon") lon: String, @Path("lat") lat: String): Call 25 | 26 | @GET("/v2.5/leWWGdduHOh6bAkw/{lon},{lat}/realtime.json") 27 | fun getRealtime(@Path("lon") lon: String, @Path("lat") lat: String): Call 28 | } -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/map/RainData.kt: -------------------------------------------------------------------------------- 1 | package com.eiffelyk.weather.weizi.map 2 | 3 | import android.graphics.Bitmap 4 | 5 | /** 6 | *
 7 |  *     author : xiaweizi
 8 |  *     class  : com.eiffelyk.weather.weizi.map.MinuteData
 9 |  *     e-mail : 1012126908@qq.com
10 |  *     time   : 2020/11/08
11 |  *     desc   :
12 |  * 
13 | */ 14 | data class RainData( 15 | var leftLat: Double, 16 | var leftLong: Double, 17 | var rightLat: Double, 18 | var rightLong: Double, 19 | var srcBitmap: Bitmap? = null, 20 | var srcUrl: String, 21 | var time: String, 22 | ) -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/map/WeatherAllData.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.map 2 | 3 | /** 4 | *
 5 |  *     author : xiaweizi
 6 |  *     class  : com.example.flutter_dynamic_weather.map.MinuteData
 7 |  *     e-mail : 1012126908@qq.com
 8 |  *     time   : 2020/11/15
 9 |  *     desc   :
10 |  * 
11 | */ 12 | data class WeatherAllData( 13 | val minuteDesc: String, 14 | val precipitation2h: List, 15 | val weatherDesc: String, 16 | val temp: String, 17 | val aqiDesc: String, 18 | val updateTimeDesc: String, 19 | val updateTime: Long, 20 | ) -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_dynamic_weather/widget/BigWidgetSettingActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_dynamic_weather.widget 2 | 3 | import android.app.Activity 4 | import android.appwidget.AppWidgetManager 5 | import android.content.Intent 6 | import androidx.appcompat.app.AppCompatActivity 7 | import android.os.Bundle 8 | import android.widget.RemoteViews 9 | import com.example.flutter_dynamic_weather.R 10 | import com.example.flutter_dynamic_weather.base.BaseActivity 11 | 12 | class BigWidgetSettingActivity : BaseActivity() { 13 | 14 | private var appWidgetId: Int = AppWidgetManager.INVALID_APPWIDGET_ID 15 | override fun initView() { 16 | super.initView() 17 | appWidgetId = intent?.extras?.getInt( 18 | AppWidgetManager.EXTRA_APPWIDGET_ID, 19 | AppWidgetManager.INVALID_APPWIDGET_ID 20 | ) ?: AppWidgetManager.INVALID_APPWIDGET_ID 21 | // onCancelConfig() 22 | } 23 | 24 | override fun getLayoutId() = R.layout.activity_big_widget_setting 25 | 26 | override fun onBackPressed() { 27 | onCompleteConfig() 28 | } 29 | 30 | private fun onCompleteConfig() { 31 | val appWidgetManager: AppWidgetManager = AppWidgetManager.getInstance(this) 32 | RemoteViews(packageName, R.layout.layout_widget_big).also { views-> 33 | appWidgetManager.updateAppWidget(appWidgetId, views) 34 | } 35 | val resultValue = Intent().apply { 36 | putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) 37 | } 38 | setResult(Activity.RESULT_OK, resultValue) 39 | finish() 40 | } 41 | 42 | private fun onCancelConfig() { 43 | val resultValue = Intent().apply { 44 | putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) 45 | } 46 | setResult(Activity.RESULT_CANCELED, resultValue) 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-nodpi/example_appwidget_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/drawable-nodpi/example_appwidget_preview.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/shape_minute_bottom_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/shape_minute_progress_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/layout/activity_big_widget_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/layout/layout_jike.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 17 | 18 | -------------------------------------------------------------------------------- /android/app/src/main/res/layout/layout_public_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /android/app/src/main/res/layout/layout_widget_big.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/layout/layout_zhuge.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 17 | 18 | -------------------------------------------------------------------------------- /android/app/src/main/res/layout/weather_anim_widget.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/cloud.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/cloud.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_common_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/ic_common_location.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_common_refresh_vector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/ic_common_refresh_vector.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_rain_area_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/ic_rain_area_pause.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_rain_area_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/ic_rain_area_play.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_zoom_in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/ic_zoom_in.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_zoom_out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/ic_zoom_out.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/icon_app_widget_tools_4x1_shili.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/icon_app_widget_tools_4x1_shili.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/icon_app_widget_tools_4x2_shili.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/icon_app_widget_tools_4x2_shili.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/icon_location.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/icon_location.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/intensity_of_rainfall_legend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/intensity_of_rainfall_legend.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/lightning0.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/lightning0.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/lightning1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/lightning1.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/lightning2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/lightning2.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/lightning3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/lightning3.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/lightning4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/lightning4.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/rain.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/rain.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/zx_left_drawer_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/android/app/src/main/res/mipmap-xxhdpi/zx_left_drawer_back.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #fff 5 | 6 | #0000 7 | #000 8 | #666666 9 | #E3E3E3 10 | #4277FF 11 | #fdfdfd 12 | #f0f0f0 13 | 14 | #33C55C45 15 | #C55C45 16 | #807272 17 | #992196F3 18 | #002196F3 19 | 20 | #33BF5256 21 | #2196F3 22 | #802196F3 23 | #032196F3 24 | #FFE1F5FE 25 | #FF81D4FA 26 | #FF039BE5 27 | #FF01579B 28 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15.27dp 4 | 6.18dp 5 | 6 | 100dp 7 | 45dp 8 | 12dp 9 | 250dp 10 | 12dp 11 | 12 | 45dp 13 | 10dp 14 | 25dp 15 | 35dp 16 | 12dp 17 | 18 | 22 | 0dp 23 | 250dp 24 | 110dp 25 | 26 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | EXAMPLE 3 | Add widget 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | 24 | 25 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/big_widget_config.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/filepaths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/network_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/weather_anim_widget_info.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext{ 3 | kotlin_version = '1.4.10' 4 | retrofit_version = '2.5.0' 5 | lifecycle_version = '2.2.0' 6 | ext.glideVersion = '4.11.0' 7 | } 8 | repositories { 9 | google() 10 | jcenter() 11 | } 12 | 13 | dependencies { 14 | classpath "com.android.tools.build:gradle:4.0.2" 15 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | google() 22 | jcenter() 23 | } 24 | } 25 | 26 | rootProject.buildDir = '../build' 27 | subprojects { 28 | project.buildDir = "${rootProject.buildDir}/${project.name}" 29 | } 30 | subprojects { 31 | project.evaluationDependsOn(':app') 32 | } 33 | 34 | task clean(type: Delete) { 35 | delete rootProject.buildDir 36 | } 37 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /assets/images/caiyun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/caiyun.png -------------------------------------------------------------------------------- /assets/images/carWashing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/carWashing.png -------------------------------------------------------------------------------- /assets/images/coldRisk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/coldRisk.png -------------------------------------------------------------------------------- /assets/images/comfort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/comfort.png -------------------------------------------------------------------------------- /assets/images/dressing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/dressing.png -------------------------------------------------------------------------------- /assets/images/empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/empty.png -------------------------------------------------------------------------------- /assets/images/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/error.png -------------------------------------------------------------------------------- /assets/images/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/ic_launcher.png -------------------------------------------------------------------------------- /assets/images/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/play.png -------------------------------------------------------------------------------- /assets/images/typhoon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/typhoon.png -------------------------------------------------------------------------------- /assets/images/ultraviolet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/ultraviolet.png -------------------------------------------------------------------------------- /assets/images/weather/cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/weather/cloudy.png -------------------------------------------------------------------------------- /assets/images/weather/cloudy_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/weather/cloudy_night.png -------------------------------------------------------------------------------- /assets/images/weather/foggy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/weather/foggy.png -------------------------------------------------------------------------------- /assets/images/weather/heavy_rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/weather/heavy_rain.png -------------------------------------------------------------------------------- /assets/images/weather/heavy_snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/weather/heavy_snow.png -------------------------------------------------------------------------------- /assets/images/weather/middle_rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/weather/middle_rain.png -------------------------------------------------------------------------------- /assets/images/weather/middle_snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/weather/middle_snow.png -------------------------------------------------------------------------------- /assets/images/weather/overcast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/weather/overcast.png -------------------------------------------------------------------------------- /assets/images/weather/sandy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/weather/sandy.png -------------------------------------------------------------------------------- /assets/images/weather/small_rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/weather/small_rain.png -------------------------------------------------------------------------------- /assets/images/weather/small_snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/weather/small_snow.png -------------------------------------------------------------------------------- /assets/images/weather/sunny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/weather/sunny.png -------------------------------------------------------------------------------- /assets/images/weather/sunny_night.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/weather/sunny_night.png -------------------------------------------------------------------------------- /assets/images/weather/sunrise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/weather/sunrise.png -------------------------------------------------------------------------------- /assets/images/weather/sunset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/assets/images/weather/sunset.png -------------------------------------------------------------------------------- /images/check.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/images/check.gif -------------------------------------------------------------------------------- /images/grid.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/images/grid.gif -------------------------------------------------------------------------------- /images/home.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/images/home.gif -------------------------------------------------------------------------------- /images/list.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/images/list.gif -------------------------------------------------------------------------------- /images/page.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/images/page.gif -------------------------------------------------------------------------------- /images/qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/images/qrcode.png -------------------------------------------------------------------------------- /images/weather1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/images/weather1.png -------------------------------------------------------------------------------- /images/weather2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/images/weather2.png -------------------------------------------------------------------------------- /images/weather3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/images/weather3.png -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - amap_core_fluttify (0.0.1): 3 | - AMapFoundation (~> 1.6) 4 | - Flutter 5 | - foundation_fluttify 6 | - amap_location_fluttify (0.0.1): 7 | - amap_core_fluttify 8 | - AMapLocation (~> 2.6.5) 9 | - Flutter 10 | - foundation_fluttify 11 | - AMapFoundation (1.6.4) 12 | - AMapLocation (2.6.5): 13 | - AMapFoundation (~> 1.6.3) 14 | - core_location_fluttify (0.0.1): 15 | - Flutter 16 | - Flutter (1.0.0) 17 | - flutter_tts (0.0.1): 18 | - Flutter 19 | - foundation_fluttify (0.0.1): 20 | - Flutter 21 | - "location_permissions (3.0.0+1)": 22 | - Flutter 23 | - package_info (0.0.1): 24 | - Flutter 25 | - shared_preferences (0.0.1): 26 | - Flutter 27 | - umeng_analytics_plugin (0.0.1): 28 | - Flutter 29 | - UMengAnalytics-NO-IDFA 30 | - UMengAnalytics-NO-IDFA (4.2.5) 31 | - url_launcher (0.0.1): 32 | - Flutter 33 | 34 | DEPENDENCIES: 35 | - amap_core_fluttify (from `.symlinks/plugins/amap_core_fluttify/ios`) 36 | - amap_location_fluttify (from `.symlinks/plugins/amap_location_fluttify/ios`) 37 | - core_location_fluttify (from `.symlinks/plugins/core_location_fluttify/ios`) 38 | - Flutter (from `Flutter`) 39 | - flutter_tts (from `.symlinks/plugins/flutter_tts/ios`) 40 | - foundation_fluttify (from `.symlinks/plugins/foundation_fluttify/ios`) 41 | - location_permissions (from `.symlinks/plugins/location_permissions/ios`) 42 | - package_info (from `.symlinks/plugins/package_info/ios`) 43 | - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) 44 | - umeng_analytics_plugin (from `.symlinks/plugins/umeng_analytics_plugin/ios`) 45 | - url_launcher (from `.symlinks/plugins/url_launcher/ios`) 46 | 47 | SPEC REPOS: 48 | trunk: 49 | - AMapFoundation 50 | - AMapLocation 51 | - UMengAnalytics-NO-IDFA 52 | 53 | EXTERNAL SOURCES: 54 | amap_core_fluttify: 55 | :path: ".symlinks/plugins/amap_core_fluttify/ios" 56 | amap_location_fluttify: 57 | :path: ".symlinks/plugins/amap_location_fluttify/ios" 58 | core_location_fluttify: 59 | :path: ".symlinks/plugins/core_location_fluttify/ios" 60 | Flutter: 61 | :path: Flutter 62 | flutter_tts: 63 | :path: ".symlinks/plugins/flutter_tts/ios" 64 | foundation_fluttify: 65 | :path: ".symlinks/plugins/foundation_fluttify/ios" 66 | location_permissions: 67 | :path: ".symlinks/plugins/location_permissions/ios" 68 | package_info: 69 | :path: ".symlinks/plugins/package_info/ios" 70 | shared_preferences: 71 | :path: ".symlinks/plugins/shared_preferences/ios" 72 | umeng_analytics_plugin: 73 | :path: ".symlinks/plugins/umeng_analytics_plugin/ios" 74 | url_launcher: 75 | :path: ".symlinks/plugins/url_launcher/ios" 76 | 77 | SPEC CHECKSUMS: 78 | amap_core_fluttify: bc2e4245c6a6d869727ed16b009a9fa3dbefcf4a 79 | amap_location_fluttify: b273a9b719a7f877ba426d1c6aeadd520e0bf7b6 80 | AMapFoundation: 3638198dfa40be54648570c3520edefad288286b 81 | AMapLocation: c566d448285cd502c1e313063d9e2460bd4d7100 82 | core_location_fluttify: 24cc9dcd986ac7059916632d2cb3babb1568ff61 83 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec 84 | flutter_tts: 0f492aab6accf87059b72354fcb4ba934304771d 85 | foundation_fluttify: 0c45145e3fad1fb99188e4979daed5b24cd9b278 86 | location_permissions: 7e0f9aa0f60deb8ff93ddf0e2a164c7e8197bc94 87 | package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62 88 | shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d 89 | umeng_analytics_plugin: 7035dfb8c5636e50429ab935125e5a3f0d23e72e 90 | UMengAnalytics-NO-IDFA: 16666e32edce5be44ae5b14aaaa7f3582e09d6ab 91 | url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef 92 | 93 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c 94 | 95 | COCOAPODS: 1.9.1 96 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | import AMapFoundationKit 4 | 5 | @UIApplicationMain 6 | @objc class AppDelegate: FlutterAppDelegate { 7 | override func application( 8 | _ application: UIApplication, 9 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 10 | ) -> Bool { 11 | GeneratedPluginRegistrant.register(with: self) 12 | AMapServices.shared()?.apiKey = "1acd2fca2d9361152f3e77d0d7807043" 13 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "images" : [ 7 | { 8 | "idiom" : "ipad", 9 | "filename" : "icon-40.png", 10 | "scale" : "1x", 11 | "size" : "40x40" 12 | }, 13 | { 14 | "scale" : "2x", 15 | "filename" : "icon-40@2x.png", 16 | "size" : "40x40", 17 | "idiom" : "ipad" 18 | }, 19 | { 20 | "idiom" : "iphone", 21 | "size" : "60x60", 22 | "filename" : "icon-60@2x.png", 23 | "scale" : "2x" 24 | }, 25 | { 26 | "size" : "72x72", 27 | "idiom" : "ipad", 28 | "scale" : "1x", 29 | "filename" : "icon-72.png" 30 | }, 31 | { 32 | "size" : "72x72", 33 | "filename" : "icon-72@2x.png", 34 | "idiom" : "ipad", 35 | "scale" : "2x" 36 | }, 37 | { 38 | "idiom" : "ipad", 39 | "scale" : "1x", 40 | "size" : "76x76", 41 | "filename" : "icon-76.png" 42 | }, 43 | { 44 | "filename" : "icon-76@2x.png", 45 | "idiom" : "ipad", 46 | "scale" : "2x", 47 | "size" : "76x76" 48 | }, 49 | { 50 | "filename" : "icon-small-50.png", 51 | "size" : "50x50", 52 | "idiom" : "ipad", 53 | "scale" : "1x" 54 | }, 55 | { 56 | "idiom" : "ipad", 57 | "filename" : "icon-small-50@2x.png", 58 | "scale" : "2x", 59 | "size" : "50x50" 60 | }, 61 | { 62 | "filename" : "icon-small.png", 63 | "idiom" : "iphone", 64 | "scale" : "1x", 65 | "size" : "29x29" 66 | }, 67 | { 68 | "size" : "29x29", 69 | "scale" : "2x", 70 | "idiom" : "iphone", 71 | "filename" : "icon-small@2x.png" 72 | }, 73 | { 74 | "idiom" : "iphone", 75 | "size" : "57x57", 76 | "scale" : "1x", 77 | "filename" : "icon.png" 78 | }, 79 | { 80 | "scale" : "2x", 81 | "idiom" : "iphone", 82 | "size" : "57x57", 83 | "filename" : "icon@2x.png" 84 | }, 85 | { 86 | "idiom" : "iphone", 87 | "scale" : "3x", 88 | "filename" : "icon-small@3x.png", 89 | "size" : "29x29" 90 | }, 91 | { 92 | "idiom" : "iphone", 93 | "filename" : "icon-40@3x.png", 94 | "scale" : "3x", 95 | "size" : "40x40" 96 | }, 97 | { 98 | "filename" : "icon-60@3x.png", 99 | "size" : "60x60", 100 | "idiom" : "iphone", 101 | "scale" : "3x" 102 | }, 103 | { 104 | "filename" : "icon-40@2x.png", 105 | "idiom" : "iphone", 106 | "scale" : "2x", 107 | "size" : "40x40" 108 | }, 109 | { 110 | "filename" : "icon-small.png", 111 | "size" : "29x29", 112 | "scale" : "1x", 113 | "idiom" : "ipad" 114 | }, 115 | { 116 | "size" : "29x29", 117 | "filename" : "icon-small@2x.png", 118 | "idiom" : "ipad", 119 | "scale" : "2x" 120 | }, 121 | { 122 | "size" : "83.5x83.5", 123 | "scale" : "2x", 124 | "idiom" : "ipad", 125 | "filename" : "icon-83.5@2x.png" 126 | }, 127 | { 128 | "scale" : "2x", 129 | "idiom" : "iphone", 130 | "filename" : "notification-icon@2x.png", 131 | "size" : "20x20" 132 | }, 133 | { 134 | "idiom" : "iphone", 135 | "scale" : "3x", 136 | "filename" : "notification-icon@3x.png", 137 | "size" : "20x20" 138 | }, 139 | { 140 | "idiom" : "ipad", 141 | "scale" : "1x", 142 | "filename" : "notification-icon~ipad.png", 143 | "size" : "20x20" 144 | }, 145 | { 146 | "idiom" : "ipad", 147 | "scale" : "2x", 148 | "size" : "20x20", 149 | "filename" : "notification-icon~ipad@2x.png" 150 | }, 151 | { 152 | "filename" : "ios-marketing.png", 153 | "scale" : "1x", 154 | "idiom" : "ios-marketing", 155 | "size" : "1024x1024" 156 | } 157 | ] 158 | } -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-72.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-72@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-small-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-small-50.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-small-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-small-50@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-small.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-small@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon-small@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/icon@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/ios-marketing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/ios-marketing.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/notification-icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/notification-icon@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/notification-icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/notification-icon@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/AppIcon.appiconset/notification-icon~ipad@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "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/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaweizi/SimplicityWeather/b40c28aeb03a7a22ca5efa60f852911d456ac88c/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 | 简悦天气 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 | NSLocationWhenInUseUsageDescription 45 | 需要定位权限 46 | LSApplicationQueriesSchemes 47 | 48 | iosamap 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/app/res/analytics_constant.dart: -------------------------------------------------------------------------------- 1 | class AnalyticsConstant { 2 | static const String initMainPage = "init_home_page"; 3 | static const String cityTotalCount = "city_total_count"; 4 | static const String locatedCityName = "located_city_name"; 5 | static const String searchCityName = "search_city_name"; 6 | static const String bottomSheet = "bottom_sheet"; 7 | static const String aboutClick = "about_click"; 8 | static const String pageShow = "page_show"; 9 | static const String weatherType = "weather_type"; 10 | static const String ota = "ota"; 11 | static const String aboutWeatherClick = "aboutWeatherClick"; 12 | static const String exampleClick = "example_click"; 13 | 14 | } -------------------------------------------------------------------------------- /lib/app/res/common_extension.dart: -------------------------------------------------------------------------------- 1 | extension StringExtension on String { 2 | DateTime get dateTime => DateTime.parse(this.substring(0, this.length - 6)); // 对时间进行裁剪 3 | } 4 | 5 | extension NumExtension on int { 6 | String get gapTime => this < 10 ? "0$this" : "$this"; // 缺0 补0 7 | } 8 | 9 | -------------------------------------------------------------------------------- /lib/app/res/dimen_constant.dart: -------------------------------------------------------------------------------- 1 | class DimenConstant { 2 | static const singleDayForecastHeight = 90.0; 3 | static const dayForecastMarginBottom = 20.0; 4 | static const aqiMinuteHeight = 50.0; 5 | static const aqiChartHeight = 120.0; 6 | static const realtimeMinHeight = 400.0; 7 | 8 | static const mainMarginStartEnd = 10.0; 9 | static const cardMarginStartEnd = 15.0; 10 | static const dayMiddleMargin = 15.0; 11 | static const cardRadius = 16.0; 12 | } -------------------------------------------------------------------------------- /lib/app/res/string_constant.dart: -------------------------------------------------------------------------------- 1 | class StringConstant { 2 | static const appName = "动态天气"; 3 | } -------------------------------------------------------------------------------- /lib/app/res/widget_state.dart: -------------------------------------------------------------------------------- 1 | enum WidgetState { 2 | loading, 3 | success, 4 | error, 5 | } 6 | -------------------------------------------------------------------------------- /lib/app/router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_dynamic_weather/app/res/analytics_constant.dart'; 4 | import 'package:flutter_dynamic_weather/app/utils/print_utils.dart'; 5 | import 'package:flutter_dynamic_weather/example/anim_view.dart'; 6 | import 'package:flutter_dynamic_weather/example/grid_view.dart'; 7 | import 'package:flutter_dynamic_weather/example/list_view.dart'; 8 | import 'package:flutter_dynamic_weather/example/main.dart'; 9 | import 'package:flutter_dynamic_weather/example/page_view.dart'; 10 | import 'package:flutter_dynamic_weather/views/pages/about/about_page.dart'; 11 | import 'package:flutter_dynamic_weather/views/pages/manager/manager_page.dart'; 12 | import 'package:flutter_dynamic_weather/views/pages/search/search_page.dart'; 13 | import 'package:umeng_analytics_plugin/umeng_analytics_plugin.dart'; 14 | 15 | import 'utils/router_utils.dart'; 16 | 17 | class AppAnalysis extends NavigatorObserver { 18 | @override 19 | void didPush(Route route, Route previousRoute) { 20 | if (route.settings.name != null) { 21 | weatherPrint("AppAnalysis didPush: ${route.settings.name}"); 22 | UmengAnalyticsPlugin.event(AnalyticsConstant.pageShow, 23 | label: "${route.settings.name}"); 24 | UmengAnalyticsPlugin.pageStart(route.settings.name); 25 | } 26 | } 27 | 28 | @override 29 | void didPop(Route route, Route previousRoute) { 30 | if (route.settings.name != null) { 31 | weatherPrint("AppAnalysis didPop: ${route.settings.name}"); 32 | UmengAnalyticsPlugin.pageEnd(route.settings.name); 33 | } 34 | } 35 | } 36 | 37 | class WeatherRouter { 38 | static const String CHANNEL_NAME = 'com.example.flutter_dynamic_weather/router'; 39 | 40 | static const String manager = 'manager'; 41 | static const String search = 'search'; 42 | static const String about = 'about'; 43 | static const String example = 'example'; 44 | static const String minute = 'minute'; 45 | static const String zhuge = 'zhuge'; 46 | static const String jike = 'jike'; 47 | 48 | static const String routePage = "page"; 49 | static const String routeList = "list"; 50 | static const String routeGrid = "grid"; 51 | static const String routeAnim = "anim"; 52 | 53 | static Future jumpToNativePage(String name) async { 54 | MethodChannel channel = MethodChannel(CHANNEL_NAME); 55 | channel.invokeMethod("startActivity", {"name": name}); 56 | } 57 | 58 | static Route generateRoute(RouteSettings settings) { 59 | switch (settings.name) { 60 | //根据名称跳转相应页面 61 | case search: 62 | return FadeRouter( 63 | child: SearchPage(), settings: RouteSettings(name: search)); 64 | 65 | case manager: 66 | return FadeRouter( 67 | child: ManagerPage(), settings: RouteSettings(name: manager)); 68 | case about: 69 | return FadeRouter(child: AboutPage(), settings: settings); 70 | case example: 71 | return FadeRouter(child: MyExampleApp(), settings: settings); 72 | case routePage: 73 | return FadeRouter(child: PageViewWidget(), settings: settings); 74 | case routeList: 75 | return FadeRouter(child: ListViewWidget(), settings: settings); 76 | case routeGrid: 77 | return FadeRouter(child: GridViewWidget(), settings: settings); 78 | case routeAnim: 79 | return FadeRouter(child: AnimViewWidget(), settings: settings); 80 | default: 81 | return MaterialPageRoute( 82 | builder: (_) => Scaffold( 83 | body: Center( 84 | child: Text('No route defined for ${settings.name}'), 85 | ), 86 | )); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/app/utils/color_utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_dynamic_weather/app/utils/print_utils.dart'; 5 | 6 | //Color randomColor(){/// 用来返回一个随机色 7 | //var random=Random(); 8 | //var a = random.nextInt(256);//透明度值 9 | //var r = random.nextInt(256);//红值 10 | //var g = random.nextInt(256);//绿值 11 | //var b = random.nextInt(256);//蓝值 12 | //return Color.fromARGB(a, r, g, b);//生成argb模式的颜色 13 | //} 14 | 15 | //Color randomColor(int limitA){ 16 | // var random=Random(); 17 | // var a = limitA+random.nextInt(256-limitA);//透明度值 18 | // var r = random.nextInt(256);//红值 19 | // var g = random.nextInt(256);//绿值 20 | // var b = random.nextInt(256);//蓝值 21 | // return Color.fromARGB(a, r, g, b);//生成argb模式的颜色 22 | //} 23 | 24 | class ColorUtils { 25 | 26 | /// 使用方法: 27 | /// var color1=ColorUtils.parse("#33428A43"); 28 | /// var color2=ColorUtils.parse("33428A43"); 29 | /// var color3=ColorUtils.parse("#428A43"); 30 | ///var color4=ColorUtils.parse("428A43"); 31 | /// 32 | static Color parse(String code) { 33 | Color result =Colors.red; 34 | var value = 0 ; 35 | if (code.contains("#")) { 36 | try { 37 | value = int.parse(code.substring(1), radix: 16); 38 | } catch (e) { 39 | weatherPrint(e.toString()); 40 | } 41 | switch (code.length) { 42 | case 1 + 6://6位 43 | result = Color(value + 0xFF000000); 44 | break; 45 | case 1 + 8://8位 46 | result = Color(value); 47 | break; 48 | default: 49 | result =Colors.red; 50 | } 51 | }else { 52 | try { 53 | value = int.parse(code, radix: 16); 54 | } catch (e) { 55 | weatherPrint(e.toString()); 56 | } 57 | switch (code.length) { 58 | case 6: 59 | result = Color(value + 0xFF000000); 60 | break; 61 | case 8: 62 | result = Color(value); 63 | break; 64 | default: 65 | result =Colors.red; 66 | } 67 | } 68 | return result; 69 | } 70 | 71 | static String colorString(Color color) => 72 | "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; 73 | } 74 | 75 | -------------------------------------------------------------------------------- /lib/app/utils/image_utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'dart:ui' as ui; 3 | import 'dart:typed_data'; 4 | 5 | import 'package:flutter/services.dart'; 6 | 7 | class ImageUtils { 8 | static Future getImage(String asset) async { 9 | ByteData data = await rootBundle.load(asset); 10 | Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List()); 11 | FrameInfo fi = await codec.getNextFrame(); 12 | return fi.image; 13 | } 14 | } -------------------------------------------------------------------------------- /lib/app/utils/location_util.dart: -------------------------------------------------------------------------------- 1 | class LocationUtil { 2 | static String convertToFlag(String longitude, String latitude) { 3 | return "$longitude,$latitude"; 4 | } 5 | 6 | static List parseFlag(String flag) { 7 | return flag.split(",").toList(); 8 | } 9 | 10 | static String getCityFlag(String key) { 11 | return key.substring(0, key.lastIndexOf(",")); 12 | } 13 | 14 | static String convertCityFlag(String cityFlag, bool isLocated) { 15 | return "$cityFlag,$isLocated"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/app/utils/ota_utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_dynamic_weather/app/res/analytics_constant.dart'; 5 | import 'package:flutter_dynamic_weather/app/utils/toast.dart'; 6 | import 'package:flutter_dynamic_weather/net/weather_api.dart'; 7 | import 'package:flutter_dynamic_weather/views/app/flutter_app.dart'; 8 | import 'package:ota_update/ota_update.dart'; 9 | import 'package:package_info/package_info.dart'; 10 | import 'package:umeng_analytics_plugin/umeng_analytics_plugin.dart'; 11 | 12 | class OTAUtils { 13 | static startOTA(String url, void onData(OtaEvent event)) async { 14 | try { 15 | OtaUpdate() 16 | .execute( 17 | url, 18 | destinationFilename: 'SimplicityWeather.apk', 19 | ) 20 | .listen( 21 | (OtaEvent event) { 22 | onData(event); 23 | print('status: ${event.status}, value: ${event.value}'); 24 | if (event.status == OtaStatus.DOWNLOAD_ERROR) { 25 | ToastUtils.show("下载失败", globalKey.currentContext); 26 | UmengAnalyticsPlugin.event(AnalyticsConstant.ota, 27 | label: "download_error"); 28 | } else if (event.status == OtaStatus.INTERNAL_ERROR) { 29 | ToastUtils.show("未知失败", globalKey.currentContext); 30 | UmengAnalyticsPlugin.event(AnalyticsConstant.ota, 31 | label: "internal_error"); 32 | } else if (event.status == OtaStatus.PERMISSION_NOT_GRANTED_ERROR) { 33 | UmengAnalyticsPlugin.event(AnalyticsConstant.ota, 34 | label: "permission_not_granted_error"); 35 | ToastUtils.show("请打开权限", globalKey.currentContext); 36 | } else if (event.status == OtaStatus.INSTALLING) { 37 | UmengAnalyticsPlugin.event(AnalyticsConstant.ota, 38 | label: "installing"); 39 | ToastUtils.show("正在安装...", globalKey.currentContext); 40 | } 41 | }, 42 | ); 43 | } catch (e) { 44 | print('Failed to make OTA update. Details: $e'); 45 | } 46 | } 47 | 48 | static initOTA() async { 49 | if (!Platform.isAndroid) { 50 | return; 51 | } 52 | var otaData = await WeatherApi().getOTA(); 53 | if (otaData != null && otaData["data"] != null) { 54 | String url = otaData["data"]["url"]; 55 | String desc = otaData["data"]["desc"]; 56 | String versionName = ""; // todo 添加 versionName 的接口配置 57 | int appCode = int.parse(otaData["data"]["appCode"]); 58 | var packageInfo = await PackageInfo.fromPlatform(); 59 | var number = int.parse(packageInfo.buildNumber); 60 | if (appCode > number) { 61 | UmengAnalyticsPlugin.event(AnalyticsConstant.ota, label: "needOTA"); 62 | showOTADialog(url, desc, versionName); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/app/utils/print_utils.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | typedef WeatherPrint = void Function(String message, 4 | {int wrapWidth, String tag}); 5 | 6 | const DEBUG = true; 7 | 8 | WeatherPrint weatherPrint = debugPrintThrottled; 9 | 10 | void debugPrintThrottled(String message, {int wrapWidth, String tag}) { 11 | if (DEBUG) { 12 | debugPrint("flutter-weather: $tag: $message", wrapWidth: wrapWidth); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/app/utils/shared_preference_util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_dynamic_weather/app/utils/print_utils.dart'; 5 | import 'package:flutter_dynamic_weather/model/city_model_entity.dart'; 6 | import 'package:flutter_dynamic_weather/model/weather_model_entity.dart'; 7 | import 'package:shared_preferences/shared_preferences.dart'; 8 | 9 | class SPUtil { 10 | static SharedPreferences _sp; 11 | static const KEY_CITY_MODELS = "city_models"; 12 | static const KEY_WEATHER_MODELS = "weather_models"; 13 | 14 | static Future get sp async { 15 | _sp = _sp ?? await SharedPreferences.getInstance(); 16 | return _sp; 17 | } 18 | 19 | static Future saveCityModels(List cityModels) async { 20 | weatherPrint("saveCityModels ${cityModels?.length}", tag: "SPUtil"); 21 | var prefs = await sp; 22 | var encodeStr = json.encode(cityModels); 23 | weatherPrint('sp -encode: $encodeStr'); 24 | return prefs.setString(KEY_CITY_MODELS, encodeStr); 25 | } 26 | 27 | static Future> getCityModels() async { 28 | weatherPrint("getCityModels", tag: "SPUtil"); 29 | var prefs = await sp; 30 | var parseValue = prefs.getString(KEY_CITY_MODELS); 31 | weatherPrint('sp-get: $parseValue'); 32 | if (parseValue == null || parseValue == "") { 33 | return null; 34 | } 35 | List decodeObject = json.decode(parseValue); 36 | List model = []; 37 | decodeObject.forEach((element) { 38 | model.add(CityModel.fromJson(element)); 39 | }); 40 | return model; 41 | } 42 | 43 | static Future saveAllWeatherModels(Map allWeatherDat) async { 44 | weatherPrint("saveAllWeatherModels ${allWeatherDat?.length}", tag: "SPUtil"); 45 | var prefs = await sp; 46 | var encodeStr = json.encode(allWeatherDat); 47 | return prefs.setString(KEY_WEATHER_MODELS, encodeStr); 48 | } 49 | 50 | static Future> getAllWeatherModels() async { 51 | weatherPrint("getAllWeatherModels", tag: "SPUtil"); 52 | var prefs = await sp; 53 | var parseValue = prefs.getString(KEY_WEATHER_MODELS); 54 | if (parseValue == null || parseValue == "") { 55 | return null; 56 | } 57 | Map model = new Map.from(json.decode(parseValue)); 58 | return model; 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /lib/app/utils/time_util.dart: -------------------------------------------------------------------------------- 1 | /// create by 张风捷特烈 on 2020/6/17 2 | /// contact me by email 1981462002@qq.com 3 | /// 说明: 4 | import 'package:flutter_dynamic_weather/app/res/common_extension.dart'; 5 | 6 | final double _kMillisLimit = 1000.0; 7 | 8 | final double _kSecondsLimit = 60 * _kMillisLimit; 9 | 10 | final double _kMinutesLimit = 60 * _kSecondsLimit; 11 | 12 | final double _kHourLimit = 24 * _kMinutesLimit; 13 | 14 | final double _kDaysLimit = 30 * _kHourLimit; 15 | 16 | class TimeUtil { 17 | 18 | ///日期格式转换 19 | static String time2string(DateTime date, {bool just = false}) { 20 | if (just) { 21 | return _getDateStr(date); 22 | } 23 | int subTimes = 24 | DateTime.now().millisecondsSinceEpoch - date.millisecondsSinceEpoch; 25 | if (subTimes < _kMillisLimit) { 26 | return "刚刚"; 27 | } else if (subTimes < _kSecondsLimit) { 28 | return (subTimes / _kMillisLimit).round().toString() + " 秒前"; 29 | } else if (subTimes < _kMinutesLimit) { 30 | return (subTimes / _kSecondsLimit).round().toString() + "分钟前"; 31 | } else if (subTimes < _kHourLimit) { 32 | return (subTimes / _kMinutesLimit).round().toString() + "小时前"; 33 | } else if (subTimes < _kDaysLimit) { 34 | return (subTimes / _kHourLimit).round().toString() + "天前"; 35 | } else { 36 | return _getDateStr(date); 37 | } 38 | } 39 | 40 | static String _getDateStr(DateTime date) { 41 | if (date == null || date.toString() == null) { 42 | return ""; 43 | } else if (date.toString().length < 10) { 44 | return date.toString(); 45 | } 46 | return date.toString().substring(0, 10); 47 | } 48 | 49 | static String getWeatherDayDesc(String time) { 50 | DateTime targetTime = time.dateTime; 51 | DateTime nowTime = DateTime.now(); 52 | if (targetTime.day == nowTime.day) { 53 | return "今天"; 54 | } else if (targetTime.day == nowTime.day + 1) { 55 | return "明天"; 56 | } else if (targetTime.day == nowTime.day - 1) { 57 | return "昨天"; 58 | } else { 59 | return "${_getWeekDes(targetTime.weekday)}"; 60 | } 61 | } 62 | 63 | static String _getWeekDes(int weekDay) { 64 | switch (weekDay) { 65 | case 1: 66 | return "周一"; 67 | case 2: 68 | return "周二"; 69 | case 3: 70 | return "周三"; 71 | case 4: 72 | return "周四"; 73 | case 5: 74 | return "周五"; 75 | case 6: 76 | return "周六"; 77 | case 7: 78 | return "周日"; 79 | } 80 | return ""; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /lib/app/utils/toast.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:toast/toast.dart'; 4 | 5 | class ToastUtils { 6 | static snap(BuildContext context, String msg, 7 | {duration = const Duration( 8 | milliseconds: 600), SnackBarAction action, Color color}) { 9 | Scaffold.of(context).showSnackBar(SnackBar( 10 | content: Text(msg), 11 | duration: duration, 12 | action: action, 13 | backgroundColor: color ?? Theme 14 | .of(context) 15 | .primaryColor, 16 | )); 17 | } 18 | 19 | static show(String msg, BuildContext context, 20 | {int duration = 1, 21 | int gravity = 2, 22 | Color backgroundColor = const Color(0xAA000000), 23 | Color textColor = Colors.white, 24 | double backgroundRadius = 20, 25 | Border border}) { 26 | Toast.show(msg, context, duration: duration, 27 | backgroundColor: backgroundColor, 28 | textColor: textColor, 29 | backgroundRadius: backgroundRadius, 30 | border: border); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/app/utils/ui_utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui' as ui; 2 | import 'package:flutter/material.dart'; 3 | 4 | class UiUtils { 5 | static ui.Paragraph getParagraph(String text, double textSize, 6 | {Color color = Colors.white, double itemWidth = 100}) { 7 | var pb = ui.ParagraphBuilder(ui.ParagraphStyle( 8 | textAlign: TextAlign.center, //居中 9 | fontSize: textSize, //大小 10 | )); 11 | pb.addText(text); 12 | pb.pushStyle(ui.TextStyle(color: color)); 13 | var paragraph = pb.build()..layout(ui.ParagraphConstraints(width: itemWidth)); 14 | return paragraph; 15 | } 16 | } -------------------------------------------------------------------------------- /lib/bloc/city/city_event.dart: -------------------------------------------------------------------------------- 1 | part of 'city_bloc.dart'; 2 | 3 | abstract class CityEvent extends Equatable { 4 | const CityEvent(); 5 | } 6 | 7 | class FetchCityDataEvent extends CityEvent { 8 | 9 | @override 10 | List get props => []; 11 | } 12 | 13 | 14 | class RequestLocationEvent extends CityEvent { 15 | 16 | const RequestLocationEvent(); 17 | 18 | @override 19 | List get props => []; 20 | } 21 | 22 | class InsertCityData extends CityEvent { 23 | final CityModel cityModel; 24 | 25 | const InsertCityData(this.cityModel); 26 | 27 | @override 28 | List get props => [cityModel]; 29 | } 30 | 31 | class DeleteCityData extends CityEvent { 32 | final String cityFlag; 33 | 34 | const DeleteCityData(this.cityFlag); 35 | 36 | @override 37 | List get props => [cityFlag]; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /lib/bloc/city/city_state.dart: -------------------------------------------------------------------------------- 1 | part of 'city_bloc.dart'; 2 | 3 | abstract class CityState extends Equatable { 4 | const CityState(); 5 | } 6 | 7 | @immutable 8 | class CitySuccess extends CityState { 9 | 10 | final List cityModels; 11 | 12 | CitySuccess(this.cityModels); 13 | 14 | @override 15 | List get props => [cityModels]; 16 | } 17 | 18 | 19 | @immutable 20 | class CityInit extends CityState { 21 | 22 | CityInit(); 23 | 24 | @override 25 | List get props => []; 26 | } 27 | 28 | @immutable 29 | class LocationSuccessState extends CityState { 30 | 31 | LocationSuccessState(); 32 | 33 | @override 34 | List get props => []; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /lib/bloc/weather/weather_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | 4 | import 'package:bloc/bloc.dart'; 5 | import 'package:equatable/equatable.dart'; 6 | import 'package:flutter_dynamic_weather/app/res/analytics_constant.dart'; 7 | import 'package:flutter_dynamic_weather/app/utils/location_util.dart'; 8 | import 'package:flutter_dynamic_weather/app/utils/print_utils.dart'; 9 | import 'package:flutter_dynamic_weather/app/utils/shared_preference_util.dart'; 10 | import 'package:flutter_dynamic_weather/event/change_index_envent.dart'; 11 | import 'package:flutter_dynamic_weather/model/city_model_entity.dart'; 12 | import 'package:flutter_dynamic_weather/model/weather_model_entity.dart'; 13 | import 'package:flutter_dynamic_weather/net/weather_api.dart'; 14 | import 'package:flutter_dynamic_weather/views/app/flutter_app.dart'; 15 | import 'package:umeng_analytics_plugin/umeng_analytics_plugin.dart'; 16 | 17 | part 'weather_event.dart'; 18 | 19 | part 'weather_state.dart'; 20 | 21 | class WeatherBloc extends Bloc { 22 | final WeatherApi weatherApi; 23 | 24 | WeatherBloc(this.weatherApi) : super(WeatherLoading(null)); 25 | 26 | @override 27 | Stream mapEventToState( 28 | WeatherEvent event, 29 | ) async* { 30 | weatherPrint('WeatherBloc || mapEventToState event: ${event.runtimeType}'); 31 | if (event is FetchWeatherDataEvent) { 32 | yield* _mapFetchWeatherToState(event.cityModel); 33 | } 34 | } 35 | 36 | Stream _mapFetchWeatherToState(CityModel cityModel) async* { 37 | weatherPrint("开始请求 ${cityModel.city} 城市的数据....flag: ${cityModel.cityFlag}"); 38 | yield WeatherLoading(cityModel); 39 | final resData = await weatherApi.loadWeatherData( 40 | LocationUtil.parseFlag(cityModel.cityFlag)[0], 41 | LocationUtil.parseFlag(cityModel.cityFlag)[1]); 42 | 43 | var allWeatherData = await SPUtil.getAllWeatherModels(); 44 | if (allWeatherData == null || allWeatherData.isEmpty) { 45 | allWeatherData = {}; 46 | } 47 | if (resData == null) { 48 | weatherPrint("城市获取失败"); 49 | yield WeatherFailed(cityModel); 50 | } else { 51 | var resDataStr = json.encode(resData); 52 | if (cityModel.isLocated == true && allWeatherData.isNotEmpty) { 53 | String needRemoveKey = ""; 54 | allWeatherData.forEach((key, value) { 55 | if (key.contains("true")) { 56 | needRemoveKey = key; 57 | return; 58 | } 59 | }); 60 | if (needRemoveKey != "") { 61 | weatherPrint('需要先移除定位城市 key: $needRemoveKey'); 62 | allWeatherData.remove(needRemoveKey); 63 | } 64 | } 65 | // 如果是定位城市,需要先移除定位城市,然后在添加 66 | allWeatherData[LocationUtil.convertCityFlag(cityModel.cityFlag, cityModel.isLocated)] = resDataStr; 67 | await SPUtil.saveAllWeatherModels(allWeatherData); 68 | 69 | final weatherData = WeatherModelEntity().fromJson(resData); 70 | if (weatherData == null) { 71 | weatherPrint("城市获取失败"); 72 | yield WeatherFailed(cityModel); 73 | } else { 74 | weatherPrint("城市获取成功"); 75 | eventBus.fire(MainBgChangeEvent()); 76 | yield WeatherSuccess(weatherData, cityModel); 77 | } 78 | } 79 | 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/bloc/weather/weather_event.dart: -------------------------------------------------------------------------------- 1 | part of 'weather_bloc.dart'; 2 | 3 | abstract class WeatherEvent extends Equatable { 4 | const WeatherEvent(); 5 | } 6 | 7 | class FetchWeatherDataEvent extends WeatherEvent { 8 | 9 | final CityModel cityModel; 10 | 11 | const FetchWeatherDataEvent(this.cityModel); 12 | 13 | @override 14 | List get props => [cityModel]; 15 | } 16 | -------------------------------------------------------------------------------- /lib/bloc/weather/weather_state.dart: -------------------------------------------------------------------------------- 1 | part of 'weather_bloc.dart'; 2 | 3 | abstract class WeatherState extends Equatable { 4 | const WeatherState(); 5 | } 6 | 7 | class WeatherLoading extends WeatherState { 8 | final CityModel cityModel; 9 | 10 | const WeatherLoading(this.cityModel); 11 | 12 | @override 13 | List get props => [cityModel]; 14 | } 15 | 16 | class WeatherFailed extends WeatherState{ 17 | final CityModel cityModel; 18 | 19 | const WeatherFailed(this.cityModel); 20 | 21 | @override 22 | List get props => [cityModel]; 23 | } 24 | 25 | class WeatherSuccess extends WeatherState{ 26 | 27 | final WeatherModelEntity entity; 28 | final CityModel cityModel; 29 | 30 | const WeatherSuccess(this.entity, this.cityModel); 31 | 32 | @override 33 | List get props => [entity, cityModel]; 34 | } -------------------------------------------------------------------------------- /lib/event/change_index_envent.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_dynamic_weather/model/city_model_entity.dart'; 2 | import 'package:flutter_dynamic_weather/views/pages/home/real_time_temp.dart'; 3 | import 'package:flutter_dynamic_weather/views/pages/search/search_page.dart'; 4 | 5 | class ChangeMainAppBarIndexEvent { 6 | final int index; 7 | final String cityFlag; 8 | 9 | ChangeMainAppBarIndexEvent(this.index, this.cityFlag); 10 | } 11 | 12 | class ChangeCityEvent { 13 | final String cityFlag; 14 | 15 | ChangeCityEvent(this.cityFlag); 16 | } 17 | 18 | class UpdateManagerData { 19 | 20 | } 21 | 22 | class MainBgChangeEvent {} 23 | 24 | class TtsStatusEvent { 25 | final TtsState ttsState; 26 | 27 | TtsStatusEvent(this.ttsState); 28 | } -------------------------------------------------------------------------------- /lib/example/anim_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_dynamic_weather/app/res/analytics_constant.dart'; 3 | import 'package:flutter_weather_bg/bg/weather_bg.dart'; 4 | import 'package:flutter_weather_bg/utils/weather_type.dart'; 5 | import 'package:umeng_analytics_plugin/umeng_analytics_plugin.dart'; 6 | 7 | /// 主要提供两个实例 8 | /// 1. 切换天气类型时,会有过度动画 9 | /// 2. 动态改变宽高,绘制的相关逻辑同步发生改变 10 | class AnimViewWidget extends StatefulWidget { 11 | @override 12 | _AnimViewWidgetState createState() => _AnimViewWidgetState(); 13 | } 14 | 15 | class _AnimViewWidgetState extends State { 16 | WeatherType _weatherType = WeatherType.sunny; 17 | double _width = 100; 18 | double _height = 200; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | var radius = 5 + (_width - 100) / 200 * 10; 23 | 24 | return Scaffold( 25 | appBar: AppBar( 26 | title: Text("AnimView"), 27 | actions: [ 28 | PopupMenuButton( 29 | itemBuilder: (context) { 30 | return >[ 31 | ...WeatherType.values 32 | .map((e) => PopupMenuItem( 33 | value: e, 34 | child: Text("${WeatherUtil.getWeatherDesc(e)}"), 35 | )) 36 | .toList(), 37 | ]; 38 | }, 39 | initialValue: _weatherType, 40 | child: Row( 41 | mainAxisAlignment: MainAxisAlignment.center, 42 | children: [ 43 | Text("${WeatherUtil.getWeatherDesc(_weatherType)}"), 44 | Icon(Icons.more_vert) 45 | ], 46 | ), 47 | onSelected: (count) { 48 | UmengAnalyticsPlugin.event(AnalyticsConstant.aboutWeatherClick, label: "$count"); 49 | setState(() { 50 | _weatherType = count; 51 | }); 52 | }, 53 | ), 54 | ], 55 | ), 56 | body: Container( 57 | child: Column( 58 | mainAxisSize: MainAxisSize.min, 59 | children: [ 60 | Card( 61 | elevation: 7, 62 | margin: EdgeInsets.only(top: 15), 63 | shape: RoundedRectangleBorder( 64 | borderRadius: BorderRadius.circular(radius)), 65 | child: ClipPath( 66 | clipper: ShapeBorderClipper( 67 | shape: RoundedRectangleBorder( 68 | borderRadius: BorderRadius.circular(radius))), 69 | child: Container( 70 | child: WeatherBg( 71 | weatherType: _weatherType, 72 | width: _width, 73 | height: _height, 74 | ), 75 | ), 76 | ), 77 | ), 78 | SizedBox( 79 | height: 20, 80 | ), 81 | 82 | SizedBox( 83 | height: 20, 84 | ), 85 | Slider( 86 | min: 100, 87 | max: 300, 88 | label: "$_width", 89 | divisions: 200, 90 | onChanged: (value) { 91 | setState(() { 92 | _width = value; 93 | }); 94 | }, 95 | value: _width, 96 | ), 97 | SizedBox( 98 | height: 20, 99 | ), 100 | Slider( 101 | min: 200, 102 | max: 600, 103 | label: "$_height", 104 | divisions: 400, 105 | onChanged: (value) { 106 | setState(() { 107 | _height = value; 108 | }); 109 | }, 110 | value: _height, 111 | ) 112 | ], 113 | ), 114 | ), 115 | ); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /lib/example/grid_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_dynamic_weather/app/res/analytics_constant.dart'; 3 | import 'package:flutter_weather_bg/flutter_weather_bg.dart'; 4 | import 'package:flutter_weather_bg/utils/print_utils.dart'; 5 | import 'package:umeng_analytics_plugin/umeng_analytics_plugin.dart'; 6 | 7 | /// 已宫格的形式展示多样的天气效果 8 | /// 同时,支持切换列数 9 | class GridViewWidget extends StatefulWidget { 10 | @override 11 | _GridViewWidgetState createState() => _GridViewWidgetState(); 12 | } 13 | 14 | class _GridViewWidgetState extends State { 15 | int _count = 2; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return Scaffold( 20 | appBar: AppBar( 21 | title: Text("GridView"), 22 | actions: [ 23 | PopupMenuButton( 24 | itemBuilder: (context) { 25 | return >[ 26 | ...[ 27 | 1, 28 | 2, 29 | 3, 30 | 4, 31 | 5, 32 | ] 33 | .map((e) => PopupMenuItem( 34 | value: e, 35 | child: Text("$e"), 36 | )) 37 | .toList(), 38 | ]; 39 | }, 40 | onSelected: (count) { 41 | UmengAnalyticsPlugin.event(AnalyticsConstant.exampleClick, 42 | label: "grid_click_$count"); 43 | setState(() { 44 | _count = count; 45 | }); 46 | }, 47 | ) 48 | ], 49 | ), 50 | body: Container( 51 | child: GridView.count( 52 | physics: BouncingScrollPhysics(), 53 | scrollDirection: Axis.vertical, 54 | crossAxisCount: _count, 55 | childAspectRatio: 1 / 2, 56 | children: WeatherType.values 57 | .map((e) => GridItemWidget( 58 | weatherType: e, 59 | count: _count, 60 | )) 61 | .toList(), 62 | ), 63 | )); 64 | } 65 | } 66 | 67 | class GridItemWidget extends StatelessWidget { 68 | final WeatherType weatherType; 69 | final int count; 70 | 71 | GridItemWidget({Key key, this.weatherType, this.count}) : super(key: key); 72 | 73 | @override 74 | Widget build(BuildContext context) { 75 | weatherPrint("grid item size: ${MediaQuery.of(context).size}"); 76 | var radius = 20.0 - 2 * count; 77 | var margin = 10.0 - count; 78 | return Card( 79 | elevation: 6, 80 | margin: EdgeInsets.all(margin), 81 | shape: 82 | RoundedRectangleBorder(borderRadius: BorderRadius.circular(radius)), 83 | child: ClipPath( 84 | clipper: ShapeBorderClipper( 85 | shape: RoundedRectangleBorder( 86 | borderRadius: BorderRadius.circular(radius))), 87 | child: Stack( 88 | children: [ 89 | WeatherBg( 90 | weatherType: weatherType, 91 | width: MediaQuery.of(context).size.width / count, 92 | height: MediaQuery.of(context).size.width * 2, 93 | ), 94 | Center( 95 | child: Text( 96 | WeatherUtil.getWeatherDesc(weatherType), 97 | style: TextStyle( 98 | color: Colors.white, 99 | fontSize: 30 / count, 100 | fontWeight: FontWeight.bold), 101 | ), 102 | ) 103 | ], 104 | ), 105 | ), 106 | ); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /lib/example/list_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_weather_bg/bg/weather_bg.dart'; 3 | import 'package:flutter_weather_bg/utils/weather_type.dart'; 4 | 5 | class ListViewWidget extends StatefulWidget { 6 | @override 7 | _ListViewWidgetState createState() => _ListViewWidgetState(); 8 | } 9 | 10 | class _ListViewWidgetState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold( 14 | appBar: AppBar( 15 | title: Text("listView"), 16 | ), 17 | body: ListView.separated( 18 | physics: BouncingScrollPhysics(), 19 | itemBuilder: (BuildContext context, int index) { 20 | return ListItemWidget( 21 | weatherType: WeatherType.values[index], 22 | ); 23 | }, 24 | separatorBuilder: (BuildContext context, int index) { 25 | return SizedBox( 26 | height: 5, 27 | ); 28 | }, 29 | itemCount: WeatherType.values.length, 30 | ), 31 | ); 32 | } 33 | } 34 | 35 | class ListItemWidget extends StatelessWidget { 36 | final WeatherType weatherType; 37 | 38 | ListItemWidget({Key key, this.weatherType}) : super(key: key); 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return Card( 43 | elevation: 4, 44 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), 45 | margin: EdgeInsets.symmetric(horizontal: 10, vertical: 8), 46 | child: ClipPath( 47 | child: Stack( 48 | children: [ 49 | WeatherBg( 50 | weatherType: weatherType, 51 | width: MediaQuery.of(context).size.width, 52 | height: 100, 53 | ), 54 | Container( 55 | alignment: Alignment(-0.8, 0), 56 | height: 100, 57 | child: Text( 58 | "北京", 59 | style: TextStyle( 60 | color: Colors.white, 61 | fontSize: 25, 62 | fontWeight: FontWeight.bold), 63 | ), 64 | ), 65 | Container( 66 | alignment: Alignment(0.8, 0), 67 | height: 100, 68 | child: Text( 69 | WeatherUtil.getWeatherDesc(weatherType), 70 | style: TextStyle( 71 | color: Colors.white, 72 | fontSize: 25, 73 | fontWeight: FontWeight.bold), 74 | ), 75 | ) 76 | ], 77 | ), 78 | clipper: ShapeBorderClipper( 79 | shape: RoundedRectangleBorder( 80 | borderRadius: BorderRadius.all(Radius.circular(20)))), 81 | ), 82 | ); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/example/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_dynamic_weather/app/res/analytics_constant.dart'; 5 | import 'package:flutter_dynamic_weather/app/router.dart'; 6 | import 'package:flutter_dynamic_weather/example/anim_view.dart'; 7 | import 'package:flutter_dynamic_weather/example/grid_view.dart'; 8 | import 'package:flutter_dynamic_weather/example/list_view.dart'; 9 | import 'package:flutter_dynamic_weather/example/page_view.dart'; 10 | import 'package:flutter_weather_bg/bg/weather_bg.dart'; 11 | import 'package:flutter_weather_bg/flutter_weather_bg.dart'; 12 | import 'package:flutter_weather_bg/utils/print_utils.dart'; 13 | import 'package:umeng_analytics_plugin/umeng_analytics_plugin.dart'; 14 | 15 | class MyExampleApp extends StatefulWidget { 16 | @override 17 | _MyAppState createState() => _MyAppState(); 18 | } 19 | 20 | class _MyAppState extends State { 21 | @override 22 | void initState() { 23 | super.initState(); 24 | } 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return HomePage(); 29 | } 30 | } 31 | 32 | /// demo 首页布局 33 | class HomePage extends StatelessWidget { 34 | /// 创建首页 item 布局 35 | Widget _buildItem(BuildContext context, String routeName, String desc, 36 | WeatherType weatherType) { 37 | double width = MediaQuery.of(context).size.width; 38 | double marginLeft = 10.0; 39 | double marginTop = 8.0; 40 | double itemWidth = (width - marginLeft * 4) / 2; 41 | double itemHeight = itemWidth * 1.5; 42 | var radius = 10.0; 43 | return Container( 44 | width: itemWidth, 45 | height: itemHeight, 46 | child: Card( 47 | elevation: 7, 48 | margin: 49 | EdgeInsets.only(left: marginLeft, right: marginLeft, top: marginTop, bottom: marginTop), 50 | shape: 51 | RoundedRectangleBorder(borderRadius: BorderRadius.circular(radius)), 52 | child: ClipPath( 53 | clipper: ShapeBorderClipper( 54 | shape: RoundedRectangleBorder( 55 | borderRadius: BorderRadius.circular(radius))), 56 | child: Stack( 57 | children: [ 58 | WeatherBg( 59 | weatherType: weatherType, 60 | width: itemWidth, 61 | height: itemHeight, 62 | ), 63 | BackdropFilter( 64 | filter: ImageFilter.blur(sigmaX: 2.5, sigmaY: 2.5), 65 | child: InkWell( 66 | child: Center( 67 | child: Text( 68 | desc, 69 | style: TextStyle( 70 | color: Colors.white, 71 | fontWeight: FontWeight.bold, 72 | fontSize: 20), 73 | ), 74 | ), 75 | onTap: () { 76 | weatherPrint("name: $routeName"); 77 | if (routeName == WeatherRouter.zhuge || routeName == WeatherRouter.jike) { 78 | WeatherRouter.jumpToNativePage(routeName); 79 | } else { 80 | Navigator.of(context).pushNamed(routeName); 81 | } 82 | UmengAnalyticsPlugin.event(AnalyticsConstant.exampleClick, 83 | label: routeName); 84 | }, 85 | ), 86 | ), 87 | ], 88 | ), 89 | ), 90 | ), 91 | ); 92 | } 93 | 94 | @override 95 | Widget build(BuildContext context) { 96 | return Scaffold( 97 | body: SingleChildScrollView( 98 | child: Wrap( 99 | children: [ 100 | SizedBox(width: 250, height: 50,), 101 | _buildItem( 102 | context, WeatherRouter.routePage, "翻页效果", WeatherType.thunder), 103 | _buildItem(context, WeatherRouter.routeGrid, "宫格效果", 104 | WeatherType.sunnyNight), 105 | _buildItem(context, WeatherRouter.routeList, "列表效果", 106 | WeatherType.lightSnow), 107 | _buildItem( 108 | context, WeatherRouter.routeAnim, "切换效果", WeatherType.sunny), 109 | _buildItem( 110 | context, WeatherRouter.zhuge, "诸葛天气", WeatherType.cloudyNight), 111 | _buildItem( 112 | context, WeatherRouter.jike, "即刻天气", WeatherType.lightRainy), 113 | ], 114 | ), 115 | ), 116 | ); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /lib/example/page_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_weather_bg/flutter_weather_bg.dart'; 3 | import 'package:flutter_weather_bg/utils/print_utils.dart'; 4 | 5 | /// 普通的 ViewPager 展示样式 6 | class PageViewWidget extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | body: Container( 11 | child: PageView.builder( 12 | physics: BouncingScrollPhysics(), 13 | itemBuilder: (BuildContext context, int index) { 14 | weatherPrint("pageView: ${MediaQuery.of(context).size}"); 15 | return Stack( 16 | children: [ 17 | WeatherBg( 18 | weatherType: WeatherType.values[index], 19 | width: MediaQuery.of(context).size.width, 20 | height: MediaQuery.of(context).size.height, 21 | ), 22 | Center( 23 | child: Text( 24 | WeatherUtil.getWeatherDesc(WeatherType.values[index]), 25 | style: TextStyle( 26 | color: Colors.white, fontSize: 30, fontWeight: FontWeight.bold), 27 | ), 28 | ) 29 | ], 30 | ); 31 | }, 32 | itemCount: WeatherType.values.length, 33 | ), 34 | ), 35 | ); 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /lib/generated/json/base/json_field.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: non_constant_identifier_names 2 | // ignore_for_file: camel_case_types 3 | // ignore_for_file: prefer_single_quotes 4 | 5 | // This file is automatically generated. DO NOT EDIT, all your changes would be lost. 6 | 7 | class JSONField { 8 | //Specify the parse field name 9 | final String name; 10 | 11 | //Specify the time resolution format 12 | final String format; 13 | 14 | //Whether to participate in toJson 15 | final bool serialize; 16 | 17 | //Whether to participate in fromMap 18 | final bool deserialize; 19 | 20 | const JSONField({this.name, this.format, this.serialize, this.deserialize}); 21 | } 22 | -------------------------------------------------------------------------------- /lib/generated/json/district_model_entity_helper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_dynamic_weather/model/district_model_entity.dart'; 2 | 3 | districtModelEntityFromJson(DistrictModelEntity data, Map json) { 4 | if (json['status'] != null) { 5 | data.status = json['status']?.toString(); 6 | } 7 | if (json['info'] != null) { 8 | data.info = json['info']?.toString(); 9 | } 10 | if (json['count'] != null) { 11 | data.count = json['count']?.toString(); 12 | } 13 | if (json['districts'] != null) { 14 | data.districts = new List(); 15 | (json['districts'] as List).forEach((v) { 16 | data.districts.add(new DistrictModelDistrict().fromJson(v)); 17 | }); 18 | } 19 | return data; 20 | } 21 | 22 | Map districtModelEntityToJson(DistrictModelEntity entity) { 23 | final Map data = new Map(); 24 | data['status'] = entity.status; 25 | data['info'] = entity.info; 26 | data['count'] = entity.count; 27 | if (entity.districts != null) { 28 | data['districts'] = entity.districts.map((v) => v.toJson()).toList(); 29 | } 30 | return data; 31 | } 32 | 33 | districtModelDistrictFromJson(DistrictModelDistrict data, Map json) { 34 | if (json['adcode'] != null) { 35 | data.adcode = json['adcode']?.toString(); 36 | } 37 | if (json['name'] != null) { 38 | data.name = json['name']?.toString(); 39 | } 40 | if (json['center'] != null) { 41 | data.center = json['center']?.toString(); 42 | } 43 | if (json['level'] != null) { 44 | data.level = json['level']?.toString(); 45 | } 46 | if (json['districts'] != null) { 47 | data.districts = new List(); 48 | (json['districts'] as List).forEach((v) { 49 | data.districts.add(new DistrictModelDistrictsDistrict().fromJson(v)); 50 | }); 51 | } 52 | return data; 53 | } 54 | 55 | Map districtModelDistrictToJson(DistrictModelDistrict entity) { 56 | final Map data = new Map(); 57 | data['adcode'] = entity.adcode; 58 | data['name'] = entity.name; 59 | data['center'] = entity.center; 60 | data['level'] = entity.level; 61 | if (entity.districts != null) { 62 | data['districts'] = entity.districts.map((v) => v.toJson()).toList(); 63 | } 64 | return data; 65 | } 66 | 67 | districtModelDistrictsDistrictFromJson(DistrictModelDistrictsDistrict data, Map json) { 68 | if (json['adcode'] != null) { 69 | data.adcode = json['adcode']?.toString(); 70 | } 71 | if (json['name'] != null) { 72 | data.name = json['name']?.toString(); 73 | } 74 | if (json['center'] != null) { 75 | data.center = json['center']?.toString(); 76 | } 77 | if (json['level'] != null) { 78 | data.level = json['level']?.toString(); 79 | } 80 | if (json['districts'] != null) { 81 | data.districts = new List(); 82 | data.districts.addAll(json['districts']); 83 | } 84 | return data; 85 | } 86 | 87 | Map districtModelDistrictsDistrictToJson(DistrictModelDistrictsDistrict entity) { 88 | final Map data = new Map(); 89 | data['adcode'] = entity.adcode; 90 | data['name'] = entity.name; 91 | data['center'] = entity.center; 92 | data['level'] = entity.level; 93 | if (entity.districts != null) { 94 | data['districts'] = []; 95 | } 96 | return data; 97 | } -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_dynamic_weather/views/app/bloc_wrapper.dart'; 3 | import 'package:flutter_dynamic_weather/views/app/flutter_app.dart'; 4 | 5 | void main() => runApp(BlocWrapper(child: FlutterApp())); -------------------------------------------------------------------------------- /lib/model/city_data.dart: -------------------------------------------------------------------------------- 1 | class CityData { 2 | static const cityLevel = "city"; 3 | static const districtLevel = "district"; 4 | String name; 5 | String center; 6 | String level; 7 | bool isLocated; 8 | 9 | 10 | @override 11 | String toString() { 12 | return 'CityData{name: $name, center: $center, level: $level, longitude: $longitude, latitude: $latitude}'; 13 | } 14 | 15 | CityData(this.name, this.center, {this.level, this.isLocated}); 16 | 17 | get longitude { 18 | return center.split(",")[0]; 19 | } 20 | 21 | get latitude { 22 | return center.split(",")[1]; 23 | } 24 | } -------------------------------------------------------------------------------- /lib/model/city_model_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:flutter_dynamic_weather/app/utils/location_util.dart'; 3 | 4 | class CityModel extends Equatable { 5 | double latitude; 6 | double longitude; 7 | String country; // 中国 8 | String province; // 江苏省 9 | String city; // 南京市 10 | String district; // 建邺区 11 | String poiName; // 南京原力数字艺术培训中心 12 | String street; // 西城路 13 | String streetNumber; // 8号 14 | bool isLocated = false; 15 | String displayedName; 16 | 17 | String get cityFlag { 18 | return LocationUtil.convertToFlag("$longitude", "$latitude"); 19 | } 20 | 21 | CityModel( 22 | {this.latitude, 23 | this.longitude, 24 | this.country, 25 | this.province, 26 | this.city, 27 | this.district, 28 | this.poiName, 29 | this.street, 30 | this.streetNumber, 31 | this.isLocated, 32 | this.displayedName}); 33 | 34 | @override 35 | String toString() { 36 | return 'CityModel{latitude: $latitude, longitude: $longitude, displayedName: $displayedName, country: $country, province: $province, city: $city, district: $district, poiName: $poiName, street: $street, streetNumber: $streetNumber, isLocated: $isLocated}'; 37 | } 38 | 39 | Map toJson() { 40 | return { 41 | "latitude": latitude, 42 | "longitude": longitude, 43 | "country": country, 44 | "province": province, 45 | "city": city, 46 | "district": district, 47 | "poiName": poiName, 48 | "street": street, 49 | "streetNumber": streetNumber, 50 | "isLocated": isLocated, 51 | "displayedName": displayedName 52 | }; 53 | } 54 | 55 | CityModel.fromJson(Map json) { 56 | latitude = json['latitude']; 57 | longitude = json['longitude']; 58 | country = json['country']; 59 | province = json['province']; 60 | district = json['district']; 61 | poiName = json['poiName']; 62 | street = json['street']; 63 | streetNumber = json['streetNumber']; 64 | isLocated = json['isLocated']; 65 | city = json['city']; 66 | displayedName = json['displayedName']; 67 | } 68 | 69 | @override 70 | List get props => [latitude, longitude]; 71 | } 72 | -------------------------------------------------------------------------------- /lib/model/district_model_entity.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_dynamic_weather/generated/json/base/json_convert_content.dart'; 2 | 3 | class DistrictModelEntity with JsonConvert { 4 | String status; 5 | String info; 6 | String count; 7 | List districts; 8 | } 9 | 10 | class DistrictModelDistrict with JsonConvert { 11 | String adcode; 12 | String name; 13 | String center; 14 | String level; 15 | List districts; 16 | } 17 | 18 | class DistrictModelDistrictsDistrict 19 | with JsonConvert { 20 | String adcode; 21 | String name; 22 | String center; 23 | String level; 24 | List districts; 25 | } 26 | -------------------------------------------------------------------------------- /lib/net/code.dart: -------------------------------------------------------------------------------- 1 | //import 'package:gsy_github_app_flutter/common/event/http_error_event.dart'; 2 | //import 'package:gsy_github_app_flutter/common/event/index.dart'; 3 | 4 | ///错误编码 5 | class Code { 6 | ///网络错误 7 | static const NETWORK_ERROR = -1; 8 | 9 | ///网络超时 10 | static const NETWORK_TIMEOUT = -2; 11 | 12 | ///网络返回数据格式化一次 13 | static const NETWORK_JSON_EXCEPTION = -3; 14 | 15 | ///Github APi Connection refused 16 | static const GITHUB_API_REFUSED = -4; 17 | 18 | static const SUCCESS = 200; 19 | 20 | static errorHandleFunction(code, message, noTip) { 21 | if (noTip) { 22 | return message; 23 | } 24 | if(message != null && message is String && (message.contains("Connection refused") || message.contains("Connection reset"))) { 25 | code = GITHUB_API_REFUSED; 26 | } 27 | return message; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/net/net_manager.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flutter_dynamic_weather/net/code.dart'; 3 | import 'package:flutter_dynamic_weather/net/rep_result.dart'; 4 | import 'package:flutter_dynamic_weather/net/response_interceptor.dart'; 5 | const String weatherBaseUrl = "https://api.caiyunapp.com/v2.5/sas9gfwyRX2NVehl/"; 6 | const String cityBaseUrl = "https://restapi.amap.com/v3/config/district?subdistrict=1&key=请使用自己的key&keywords="; 7 | const String geoBaseUrl = "https://restapi.amap.com/v3/geocode/regeo?key=请使用自己的key&location="; 8 | const String otaBaseUrl = "http://xiaweizi.online/config/ota/"; 9 | const int _kReceiveTimeout = 15000; 10 | const int _kSendTimeout = 15000; 11 | const int _kConnectTimeout = 15000; 12 | 13 | ///http请求 14 | class NetManager { 15 | 16 | static NetManager _instance = NetManager._internal(); 17 | 18 | Dio _dio; 19 | 20 | ///通用全局单例,第一次使用时初始化 21 | NetManager._internal({String token}) { 22 | if (null == _dio) { 23 | _dio = Dio(BaseOptions( 24 | baseUrl: weatherBaseUrl, 25 | connectTimeout: _kReceiveTimeout, 26 | receiveTimeout: _kConnectTimeout, 27 | sendTimeout: _kSendTimeout, 28 | )); 29 | } 30 | _dio.interceptors.add(LogInterceptor()); 31 | _dio.interceptors.add(ResponseInterceptors()); 32 | } 33 | 34 | static NetManager getInstance() { 35 | return _instance; 36 | } 37 | 38 | baseUrl(String baseUrl) { 39 | _dio.options.baseUrl = baseUrl; 40 | return _instance; 41 | } 42 | 43 | Future get(String url, 44 | {Map header, Map param}) async { 45 | Response response; 46 | try { 47 | response = await _dio.get(url, queryParameters: param); 48 | } on DioError catch (e) { 49 | return resultError(e); 50 | } 51 | if (response.data is DioError) { 52 | return resultError(response.data); 53 | } 54 | return response.data; 55 | } 56 | 57 | resultError(DioError e) { 58 | Response errorResponse; 59 | if (e.response != null) { 60 | errorResponse = e.response; 61 | } else { 62 | errorResponse = Response(statusCode: 666); 63 | } 64 | if (e.type == DioErrorType.CONNECT_TIMEOUT || 65 | e.type == DioErrorType.RECEIVE_TIMEOUT) { 66 | errorResponse.statusCode = Code.NETWORK_TIMEOUT; 67 | } 68 | return RepResult( 69 | Code.errorHandleFunction(errorResponse.statusCode, e.message, false), 70 | false, 71 | errorResponse.statusCode); 72 | } 73 | 74 | 75 | } -------------------------------------------------------------------------------- /lib/net/rep_result.dart: -------------------------------------------------------------------------------- 1 | 2 | class RepResult { 3 | var data; 4 | bool status; 5 | int code; 6 | String msg; 7 | 8 | RepResult(this.data, this.status, this.code, {this.msg=""}); 9 | 10 | @override 11 | String toString() { 12 | return 'RepResult{data: $data, result: $status, code: $code, msg: $msg}'; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/net/response_interceptor.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:flutter_dynamic_weather/app/utils/print_utils.dart'; 3 | import 'package:flutter_dynamic_weather/net/code.dart'; 4 | import 'package:flutter_dynamic_weather/net/rep_result.dart'; 5 | 6 | /// create by 张风捷特烈 on 2020/4/28 7 | /// contact me by email 1981462002@qq.com 8 | /// 说明: 9 | /// 10 | class ResponseInterceptors extends InterceptorsWrapper { 11 | @override 12 | onResponse(Response response) async { 13 | RequestOptions option = response.request; 14 | var value; 15 | try { 16 | var header = response.headers[Headers.contentTypeHeader]; 17 | if ((header != null && header.toString().contains("text"))) { 18 | value = new RepResult(response.data, true, Code.SUCCESS); 19 | } else if (response.statusCode >= 200 && response.statusCode < 300) { 20 | value = new RepResult(response.data, true, Code.SUCCESS); 21 | } 22 | } catch (e) { 23 | weatherPrint(e.toString() + option.path); 24 | value = new RepResult(response.data, false, response.statusCode,msg: e.toString()); 25 | } 26 | return value; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/net/weather_api.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | import 'package:flutter_dynamic_weather/model/district_model_entity.dart'; 3 | import 'package:flutter_dynamic_weather/net/net_manager.dart'; 4 | 5 | class WeatherApi { 6 | Future loadWeatherData(String longitude, String latitude) async { 7 | // WeatherModelEntity data = await WeatherApi.loadWeatherData("121.6544","25.1552"); 8 | var res = 9 | await NetManager.getInstance().baseUrl(weatherBaseUrl).get("$longitude,$latitude/weather.json"); 10 | if (res != null && res.status) { 11 | return res.data; 12 | } 13 | return null; 14 | } 15 | // https://api.caiyunapp.com/v2.5/sas9gfwyRX2NVehl/121.6544,25.1552/minutely.json 16 | Future loadMinuteData(String longitude, String latitude) async { 17 | // WeatherModelEntity data = await WeatherApi.loadWeatherData("121.6544","25.1552"); 18 | var res = 19 | await NetManager.getInstance().baseUrl(weatherBaseUrl).get("$longitude,$latitude/minutely.json"); 20 | if (res != null && res.status) { 21 | return res.data; 22 | } 23 | return null; 24 | } 25 | 26 | Future searchCity(String keywords) async { 27 | // WeatherModelEntity data = await WeatherApi.loadWeatherData("121.6544","25.1552"); 28 | var res = await NetManager.getInstance().baseUrl(cityBaseUrl).get("$keywords"); 29 | if (res != null && res.status) { 30 | return DistrictModelEntity().fromJson(res.data); 31 | } 32 | return null; 33 | } 34 | 35 | Future reGeo(String location) async { 36 | // WeatherModelEntity data = await WeatherApi.loadWeatherData("121.6544","25.1552"); 37 | var res = await NetManager.getInstance().baseUrl(geoBaseUrl).get("$location"); 38 | if (res != null && res.status) { 39 | return res.data; 40 | } 41 | return null; 42 | } 43 | 44 | Future getOTA() async { 45 | var res = await NetManager.getInstance().baseUrl(otaBaseUrl).get(""); 46 | if (res != null && res.status) { 47 | return res.data; 48 | } 49 | return null; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/views/app/bloc_wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:flutter_dynamic_weather/bloc/city/city_bloc.dart'; 4 | import 'package:flutter_dynamic_weather/bloc/weather/weather_bloc.dart'; 5 | import 'package:flutter_dynamic_weather/net/weather_api.dart'; 6 | 7 | class BlocWrapper extends StatelessWidget { 8 | final Widget child; 9 | final weatherApi = WeatherApi(); 10 | 11 | BlocWrapper({@required this.child}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return MultiBlocProvider( 16 | providers: [ 17 | BlocProvider(create: (_) => CityBloc(weatherApi)..add(FetchCityDataEvent())), 18 | BlocProvider(create: (_) => WeatherBloc(weatherApi)), 19 | ], 20 | child: child, 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/views/app/flutter_app.dart: -------------------------------------------------------------------------------- 1 | import 'package:amap_location_fluttify/amap_location_fluttify.dart'; 2 | import 'package:event_bus/event_bus.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_dynamic_weather/app/res/weather_type.dart'; 6 | import 'package:flutter_dynamic_weather/app/router.dart'; 7 | import 'package:flutter_dynamic_weather/app/utils/image_utils.dart'; 8 | import 'package:flutter_dynamic_weather/views/common/loading_dialog.dart'; 9 | import 'package:flutter_dynamic_weather/views/common/ota_dialog.dart'; 10 | import 'package:flutter_dynamic_weather/views/pages/home/home_page.dart'; 11 | import 'dart:ui' as ui; 12 | 13 | import 'package:flutter_weather_bg/flutter_weather_bg.dart'; 14 | 15 | EventBus eventBus = EventBus(); 16 | GlobalKey globalKey = GlobalKey(); 17 | ValueNotifier offsetNotifier = ValueNotifier(0); 18 | Map weatherImages = {}; 19 | 20 | void showAppDialog({String loadingMsg = "正在加载中..."}) { 21 | showDialog( 22 | context: globalKey.currentContext, 23 | barrierDismissible: false, 24 | builder: (BuildContext context) { 25 | return new LoadingDialog( 26 | text: loadingMsg, 27 | ); 28 | }); 29 | } 30 | 31 | void showOTADialog(String apkUrl, String desc, String versionName) { 32 | showDialog( 33 | context: globalKey.currentContext, 34 | barrierDismissible: false, 35 | builder: (BuildContext context) { 36 | return new OTADialog(desc, versionName, apkUrl); 37 | }); 38 | } 39 | 40 | void fetchWeatherImages() async { 41 | WeatherType.values.forEach((element) async { 42 | weatherImages[element] = await ImageUtils.getImage(WeatherUtils.getWeatherIcon(element)); 43 | }); 44 | } 45 | 46 | class FlutterApp extends StatelessWidget { 47 | @override 48 | Widget build(BuildContext context) { 49 | fetchWeatherImages(); 50 | AmapLocation.instance.init(iosKey: "1acd2fca2d9361152f3e77d0d7807043"); 51 | return MaterialApp( 52 | title: "动态天气", 53 | debugShowCheckedModeBanner: false, 54 | onGenerateRoute: WeatherRouter.generateRoute, 55 | navigatorObservers: [AppAnalysis()], 56 | color: Colors.blue[600], 57 | home: AnnotatedRegion( 58 | value: SystemUiOverlayStyle.light, 59 | child: HomePage(key: globalKey), 60 | ), 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/views/bg/weather_main_bg.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_dynamic_weather/app/res/weather_type.dart'; 6 | import 'package:flutter_dynamic_weather/app/utils/location_util.dart'; 7 | import 'package:flutter_dynamic_weather/app/utils/print_utils.dart'; 8 | import 'package:flutter_dynamic_weather/app/utils/shared_preference_util.dart'; 9 | import 'package:flutter_dynamic_weather/event/change_index_envent.dart'; 10 | import 'package:flutter_dynamic_weather/model/city_model_entity.dart'; 11 | import 'package:flutter_dynamic_weather/model/weather_model_entity.dart'; 12 | import 'package:flutter_dynamic_weather/views/app/flutter_app.dart'; 13 | import 'package:flutter_weather_bg/flutter_weather_bg.dart'; 14 | 15 | class WeatherMainBg extends StatefulWidget { 16 | @override 17 | _WeatherMainBgState createState() => _WeatherMainBgState(); 18 | } 19 | 20 | class _WeatherMainBgState extends State 21 | with SingleTickerProviderStateMixin { 22 | int _index = 0; 23 | StreamSubscription _subscription; 24 | List _weatherTypes; 25 | double _value = 1; 26 | 27 | Future fetchWeatherTypes() async { 28 | weatherPrint("天气背景开始获取数据..."); 29 | List weatherTypes = []; 30 | List cityModels = await SPUtil.getCityModels(); 31 | Map allWeatherData = await SPUtil.getAllWeatherModels(); 32 | if (cityModels != null && 33 | cityModels.isNotEmpty && 34 | allWeatherData != null && 35 | allWeatherData.isNotEmpty) { 36 | cityModels.forEach((element) { 37 | String key = 38 | "${LocationUtil.convertCityFlag(element.cityFlag, element.isLocated)}"; 39 | if (allWeatherData.containsKey(key)) { 40 | WeatherType weatherType = WeatherType.sunny; 41 | var modelStr = allWeatherData[key]; 42 | if (modelStr != null && modelStr.isNotEmpty) { 43 | WeatherModelEntity weatherModelEntity = 44 | WeatherModelEntity().fromJson(json.decode(modelStr)); 45 | if (weatherModelEntity != null && 46 | weatherModelEntity.result != null && 47 | weatherModelEntity.result.realtime != null) { 48 | weatherType = WeatherUtils.convertWeatherType( 49 | weatherModelEntity.result.realtime.skycon); 50 | } 51 | } 52 | weatherTypes.add(weatherType); 53 | } 54 | }); 55 | } 56 | // weatherTypes.clear(); 57 | // weatherTypes = [WeatherType.lightSnow, WeatherType.middleSnow, WeatherType.heavySnow]; 58 | // weatherTypes[0] = WeatherType.lightSnow; 59 | // weatherTypes[1] = WeatherType.middleSnow; 60 | // weatherTypes[2] = WeatherType.heavySnow; 61 | if (weatherTypes.isNotEmpty) { 62 | setState(() { 63 | _weatherTypes = weatherTypes; 64 | if (_index >= _weatherTypes.length) { 65 | _index = _weatherTypes.length - 1; 66 | } 67 | }); 68 | } 69 | } 70 | 71 | @override 72 | void initState() { 73 | _subscription = eventBus.on().listen((event) { 74 | if (event is ChangeMainAppBarIndexEvent) { 75 | _index = event.index; 76 | if (_weatherTypes != null && 77 | _weatherTypes.isNotEmpty && 78 | _index < _weatherTypes.length) { 79 | var type = _weatherTypes[_index]; 80 | } 81 | setState(() {}); 82 | } else if (event is MainBgChangeEvent) { 83 | fetchWeatherTypes(); 84 | } 85 | }); 86 | fetchWeatherTypes(); 87 | super.initState(); 88 | } 89 | 90 | @override 91 | void dispose() { 92 | _subscription.cancel(); 93 | super.dispose(); 94 | } 95 | 96 | @override 97 | Widget build(BuildContext context) { 98 | var width = MediaQuery.of(context).size.width; 99 | var height = MediaQuery.of(context).size.height; 100 | WeatherType weatherType = WeatherType.sunny; 101 | if (_weatherTypes != null && _weatherTypes.isNotEmpty) { 102 | weatherType = _weatherTypes[_index]; 103 | } 104 | return Container( 105 | child: Stack( 106 | children: [ 107 | WeatherBg( 108 | weatherType: weatherType, 109 | width: width, 110 | height: height, 111 | ), 112 | ], 113 | ), 114 | ); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /lib/views/common/blur_rect.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_dynamic_weather/app/res/dimen_constant.dart'; 4 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 5 | 6 | class BlurRectWidget extends StatefulWidget { 7 | final Widget child; 8 | final double sigmaX; 9 | final double sigmaY; 10 | final BorderRadius borderRadius; 11 | final Color color; 12 | 13 | const BlurRectWidget({ 14 | Key key, 15 | this.child, 16 | this.sigmaX, 17 | this.sigmaY, 18 | this.borderRadius, 19 | this.color 20 | }) : super(key: key); 21 | 22 | @override 23 | _BlurRectWidgetState createState() => _BlurRectWidgetState(); 24 | } 25 | 26 | class _BlurRectWidgetState extends State { 27 | @override 28 | Widget build(BuildContext context) { 29 | return Container( 30 | child: Stack( 31 | children: [ 32 | ClipRRect( 33 | borderRadius: widget.borderRadius == null 34 | ? BorderRadius.circular(DimenConstant.cardRadius) 35 | : widget.borderRadius, 36 | child: BackdropFilter( 37 | filter: ImageFilter.blur( 38 | sigmaX: this.widget.sigmaX != null ? this.widget.sigmaX : 5, 39 | sigmaY: this.widget.sigmaY != null ? this.widget.sigmaY : 5, 40 | ), 41 | child: Container( 42 | color: this.widget.color == null ? Colors.black12 : this.widget.color, 43 | child: this.widget.child, 44 | ), 45 | ), 46 | ), 47 | 48 | ], 49 | ), 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/views/common/loading_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class LoadingDialog extends Dialog { 4 | final String text; 5 | 6 | LoadingDialog({Key key, @required this.text}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return new Material( 11 | type: MaterialType.transparency, 12 | child: new Center( 13 | child: new SizedBox( 14 | width: 120.0, 15 | height: 120.0, 16 | child: new Container( 17 | decoration: ShapeDecoration( 18 | color: Color(0xffffffff), 19 | shape: RoundedRectangleBorder( 20 | borderRadius: BorderRadius.all( 21 | Radius.circular(8.0), 22 | ), 23 | ), 24 | ), 25 | child: new Column( 26 | mainAxisAlignment: MainAxisAlignment.center, 27 | crossAxisAlignment: CrossAxisAlignment.center, 28 | children: [ 29 | new CircularProgressIndicator(), 30 | new Padding( 31 | padding: const EdgeInsets.only( 32 | top: 20.0, 33 | ), 34 | child: new Text(text), 35 | ), 36 | ], 37 | ), 38 | ), 39 | ), 40 | ), 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/views/common/loading_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_dynamic_weather/views/common/blur_rect.dart'; 3 | 4 | enum ViewState { loading, error, empty } 5 | 6 | class StateView extends StatelessWidget { 7 | final ViewState weatherState; 8 | 9 | StateView( 10 | {Key key, this.weatherState = ViewState.loading}) 11 | : super(key: key); 12 | 13 | String _getDesc() { 14 | if (weatherState == ViewState.empty) { 15 | return "暂无数据"; 16 | } else if (weatherState == ViewState.error) { 17 | return "请求失败"; 18 | } else { 19 | return "正在加载中"; 20 | } 21 | } 22 | 23 | Widget _getWidget() { 24 | if (weatherState == ViewState.empty) { 25 | return Image.asset( 26 | "assets/images/empty.png", 27 | color: Color(0xffffffff), 28 | width: 60, 29 | height: 60, 30 | ); 31 | } else if (weatherState == ViewState.error) { 32 | return Image.asset( 33 | "assets/images/error.png", 34 | color: Color(0xffffffff), 35 | width: 60, 36 | height: 60, 37 | ); 38 | } else { 39 | return CircularProgressIndicator(); 40 | } 41 | } 42 | 43 | @override 44 | Widget build(BuildContext context) { 45 | return new Material( 46 | type: MaterialType.transparency, 47 | child: new Center( 48 | child: new SizedBox( 49 | width: 120.0, 50 | height: 120.0, 51 | child: BlurRectWidget( 52 | color: Colors.black.withAlpha(100), 53 | child: new Container( 54 | width: 120.0, 55 | height: 120.0, 56 | decoration: ShapeDecoration( 57 | shape: RoundedRectangleBorder( 58 | borderRadius: BorderRadius.all( 59 | Radius.circular(8.0), 60 | ), 61 | ), 62 | ), 63 | child: new Column( 64 | mainAxisAlignment: MainAxisAlignment.center, 65 | crossAxisAlignment: CrossAxisAlignment.center, 66 | children: [ 67 | _getWidget(), 68 | new Padding( 69 | padding: const EdgeInsets.only( 70 | top: 20.0, 71 | ), 72 | child: new Text( 73 | _getDesc(), 74 | style: TextStyle( 75 | color: Color(0xffffffff), 76 | ), 77 | ), 78 | )], 79 | ), 80 | ), 81 | ), 82 | ), 83 | ), 84 | ); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/views/common/wave_view.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class WaveProgress extends StatefulWidget { 6 | final Size size; 7 | final Color fillColor; 8 | final double progress; 9 | 10 | WaveProgress(this.size, this.fillColor, this.progress); 11 | 12 | @override 13 | WaveProgressState createState() => new WaveProgressState(); 14 | } 15 | 16 | class WaveProgressState extends State 17 | with TickerProviderStateMixin { 18 | AnimationController waveController; 19 | AnimationController progressController; 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | 25 | progressController = AnimationController( 26 | vsync: this, 27 | duration: Duration(milliseconds: 3000), 28 | ); 29 | 30 | waveController = AnimationController( 31 | vsync: this, 32 | duration: Duration(milliseconds: 800), 33 | ); 34 | progressController.animateTo(widget.progress); 35 | waveController.repeat(); 36 | } 37 | 38 | @override 39 | void dispose() { 40 | waveController.dispose(); 41 | progressController.dispose(); 42 | super.dispose(); 43 | } 44 | 45 | @override 46 | void didUpdateWidget(WaveProgress oldWidget) { 47 | super.didUpdateWidget(oldWidget); 48 | if (oldWidget.progress != widget.progress) { 49 | progressController.animateTo(widget.progress / 100.0); 50 | } 51 | } 52 | 53 | @override 54 | Widget build(BuildContext context) { 55 | return new Container( 56 | width: widget.size.width, 57 | height: widget.size.height, 58 | child: new AnimatedBuilder( 59 | animation: waveController, 60 | builder: (BuildContext context, Widget child) { 61 | return new CustomPaint( 62 | painter: WaveProgressPainter( 63 | waveController, progressController, widget.fillColor)); 64 | })); 65 | } 66 | } 67 | 68 | class WaveProgressPainter extends CustomPainter { 69 | Animation _waveAnimation; 70 | Animation _progressAnimation; 71 | Color fillColor; 72 | Paint topPaint = new Paint(); 73 | Paint bottomPaint = new Paint(); 74 | 75 | WaveProgressPainter( 76 | this._waveAnimation, this._progressAnimation, this.fillColor) 77 | : super(repaint: _waveAnimation); 78 | 79 | @override 80 | void paint(Canvas canvas, Size size) { 81 | bottomPaint.color = fillColor.withOpacity(0.45); 82 | double progress = _progressAnimation.value; 83 | double frequency = 3.2; 84 | double waveHeight = 4.0; 85 | double currentHeight = (1 - progress) * size.height; 86 | 87 | Path path = Path(); 88 | path.moveTo(0.0, currentHeight); 89 | for (double i = 0.0; i < size.width; i++) { 90 | path.lineTo( 91 | i, 92 | currentHeight + 93 | sin((i / size.width * 2 * pi * frequency) + 94 | (_waveAnimation.value * 2 * pi) + 95 | pi * 1) * 96 | waveHeight); 97 | } 98 | 99 | path.lineTo(size.width, size.height); 100 | path.lineTo(0.0, size.height); 101 | path.close(); 102 | canvas.drawPath(path, bottomPaint); 103 | 104 | topPaint.color = fillColor; 105 | frequency = 1.8; 106 | waveHeight = 10.0; 107 | 108 | path.reset(); 109 | path.moveTo(0.0, currentHeight); 110 | for (double i = 0.0; i < size.width; i++) { 111 | path.lineTo( 112 | i, 113 | currentHeight + 114 | sin((i / size.width * 2 * pi * frequency) + 115 | (_waveAnimation.value * 2 * pi)) * 116 | waveHeight); 117 | } 118 | 119 | path.lineTo(size.width, size.height); 120 | path.lineTo(0.0, size.height); 121 | path.close(); 122 | canvas.drawPath(path, topPaint); 123 | } 124 | 125 | @override 126 | bool shouldRepaint(CustomPainter oldDelegate) => true; 127 | } 128 | -------------------------------------------------------------------------------- /lib/views/pages/home/aqi_chart.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_dynamic_weather/app/res/dimen_constant.dart'; 5 | import 'package:flutter_dynamic_weather/app/utils/color_utils.dart'; 6 | import 'package:flutter_dynamic_weather/app/utils/print_utils.dart'; 7 | import 'package:flutter_dynamic_weather/app/utils/ui_utils.dart'; 8 | import 'package:flutter_dynamic_weather/model/weather_model_entity.dart'; 9 | import 'package:flutter_dynamic_weather/views/common/blur_rect.dart'; 10 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 11 | import 'dart:ui' as ui; 12 | 13 | class AqiChartView extends StatelessWidget { 14 | final WeatherModelEntity entity; 15 | 16 | const AqiChartView({ 17 | Key key, 18 | @required this.entity, 19 | }) : super(key: key); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | var width = (1.sw - 24 | DimenConstant.cardMarginStartEnd * 2 - 25 | DimenConstant.dayMiddleMargin) / 26 | 2; 27 | var height = DimenConstant.aqiChartHeight; 28 | int aqiValue = entity?.result?.realtime?.airQuality?.aqi?.chn; 29 | String aqiValueStr = aqiValue == null ? "0" : "$aqiValue"; 30 | double aqiRatio = aqiValue == null ? 0 : aqiValue.toDouble() / 500; 31 | double humidityValue = entity?.result?.realtime?.humidity ?? 0; 32 | String humidityStr = "${(humidityValue * 100).toInt()}%"; 33 | 34 | return Row( 35 | children: [ 36 | BlurRectWidget( 37 | child: Container( 38 | width: width, 39 | height: height, 40 | child: CustomPaint( 41 | painter: AqiChartPainter(aqiRatio, aqiValueStr, 42 | entity?.result?.realtime?.airQuality?.description?.chn), 43 | ), 44 | ), 45 | ), 46 | SizedBox( 47 | width: DimenConstant.dayMiddleMargin, 48 | ), 49 | BlurRectWidget( 50 | child: Container( 51 | width: width, 52 | height: height, 53 | child: CustomPaint( 54 | painter: AqiChartPainter(humidityValue, humidityStr, "体感"), 55 | ), 56 | ), 57 | ), 58 | ], 59 | ); 60 | } 61 | } 62 | 63 | class AqiChartPainter extends CustomPainter { 64 | Paint _paint = Paint(); 65 | Path _path = Path(); 66 | double ratio; 67 | String value; 68 | String desc; 69 | 70 | AqiChartPainter(this.ratio, this.value, this.desc); 71 | 72 | @override 73 | void paint(Canvas canvas, Size size) { 74 | weatherPrint("AqiChartPainter size:$size"); 75 | var radius = size.height / 2 - 10; 76 | var centerX = size.width / 2; 77 | var centerY = size.height / 2; 78 | var centerOffset = Offset(centerX, centerY); 79 | // 绘制半透明圆弧 80 | _path.reset(); 81 | _path.addArc(Rect.fromCircle(center: centerOffset, radius: radius), 82 | pi * 0.7, pi * 1.6); 83 | _paint.style = PaintingStyle.stroke; 84 | _paint.strokeWidth = 4; 85 | _paint.strokeCap = StrokeCap.round; 86 | _paint.color = Colors.white38; 87 | canvas.drawPath(_path, _paint); 88 | // 绘制纯白色圆弧 89 | _path.reset(); 90 | _path.addArc(Rect.fromCircle(center: centerOffset, radius: radius), 91 | pi * 0.7, pi * 1.6 * ratio); 92 | _paint.color = Colors.white; 93 | canvas.drawPath(_path, _paint); 94 | // 绘制 AQIValue 95 | var valuePara = UiUtils.getParagraph(value, 30); 96 | canvas.drawParagraph( 97 | valuePara, 98 | Offset(centerOffset.dx - valuePara.width / 2, 99 | centerOffset.dy - valuePara.height / 2)); 100 | // 绘制 AQIDesc 101 | var descPara = UiUtils.getParagraph("$desc", 15); 102 | canvas.drawParagraph( 103 | descPara, 104 | Offset(centerOffset.dx - valuePara.width / 2, 105 | centerOffset.dy + valuePara.height / 2)); 106 | } 107 | 108 | @override 109 | bool shouldRepaint(CustomPainter oldDelegate) { 110 | return false; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /lib/views/pages/home/main_message.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:flutter_dynamic_weather/app/utils/print_utils.dart'; 6 | import 'package:flutter_dynamic_weather/bloc/city/city_bloc.dart'; 7 | import 'package:flutter_dynamic_weather/event/change_index_envent.dart'; 8 | import 'package:flutter_dynamic_weather/model/city_model_entity.dart'; 9 | import 'package:flutter_dynamic_weather/model/weather_model_entity.dart'; 10 | import 'package:flutter_dynamic_weather/views/app/flutter_app.dart'; 11 | import 'package:flutter_dynamic_weather/views/common/loading_view.dart'; 12 | import 'package:flutter_dynamic_weather/views/pages/home/city_view.dart'; 13 | 14 | class MainMessage extends StatefulWidget { 15 | @override 16 | _MainMessageState createState() => _MainMessageState(); 17 | } 18 | 19 | class _MainMessageState extends State { 20 | List _cityModels; 21 | PageController _controller = PageController(); 22 | StreamSubscription _subscription; 23 | 24 | Widget _buildMainWidget() { 25 | weatherPrint('main-build main widget'); 26 | if (_cityModels == null) { 27 | return StateView(weatherState: ViewState.loading); 28 | } else if (_cityModels.isEmpty) { 29 | return StateView(weatherState: ViewState.empty); 30 | } else { 31 | weatherPrint("创建城市列表页面"); 32 | weatherPrint('main-success, cityModel: $_cityModels'); 33 | return PageView.builder( 34 | controller: _controller, 35 | onPageChanged: (index) { 36 | weatherPrint('current index: $index'); 37 | eventBus.fire(ChangeMainAppBarIndexEvent(index, _cityModels[index].cityFlag)); 38 | }, 39 | itemBuilder: (context, index) { 40 | return CityView( 41 | cityModel: _cityModels[index], 42 | ); 43 | }, 44 | itemCount: _cityModels.length, 45 | ); 46 | } 47 | } 48 | 49 | @override 50 | void initState() { 51 | _subscription = eventBus.on().listen((event) { 52 | if (_cityModels != null) { 53 | _cityModels.forEach((element) { 54 | if (element.cityFlag == event.cityFlag) { 55 | var index = _cityModels.indexOf(element); 56 | if (index >= 0 && index < _cityModels.length) { 57 | _controller.jumpToPage(index); 58 | } 59 | } 60 | }); 61 | } 62 | }); 63 | super.initState(); 64 | } 65 | 66 | @override 67 | void dispose() { 68 | _subscription.cancel(); 69 | super.dispose(); 70 | } 71 | 72 | @override 73 | Widget build(BuildContext context) { 74 | var state = BlocProvider.of(context).state; 75 | weatherPrint('build || main-state: ${state.runtimeType}'); 76 | if (state is CitySuccess) { 77 | _cityModels = state.cityModels; 78 | } 79 | return BlocListener( 80 | listener: (_, state) { 81 | weatherPrint('BlocListener || main-state: ${state.runtimeType}'); 82 | if (state is CitySuccess) { 83 | setState(() { 84 | _cityModels = state.cityModels; 85 | }); 86 | } 87 | }, 88 | child: _buildMainWidget(), 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/views/pages/home/real_time_detail.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_dynamic_weather/app/res/dimen_constant.dart'; 3 | import 'package:flutter_dynamic_weather/app/utils/color_utils.dart'; 4 | import 'package:flutter_dynamic_weather/model/weather_model_entity.dart'; 5 | import 'package:flutter_dynamic_weather/views/common/blur_rect.dart'; 6 | import 'package:flutter_dynamic_weather/views/pages/home/sun_rise_set.dart'; 7 | import 'package:flutter_screenutil/flutter_screenutil.dart'; 8 | 9 | class RealTimeDetailView extends StatelessWidget { 10 | final WeatherModelEntity entity; 11 | 12 | const RealTimeDetailView({ 13 | Key key, 14 | @required this.entity, 15 | }) : super(key: key); 16 | 17 | Widget _buildSunSetRiseWidget() { 18 | List astro = entity?.result?.daily?.astro; 19 | if (astro == null || astro.isEmpty) { 20 | return Container(); 21 | } 22 | return SunSetRiseView(model: entity?.result?.daily?.astro[0]); 23 | } 24 | 25 | Widget _buildItem(String title, String desc) { 26 | var width = (1.sw - DimenConstant.cardMarginStartEnd * 2 - 60) / 2; 27 | return Container( 28 | width: width, 29 | height: width / 2, 30 | child: Column( 31 | crossAxisAlignment: CrossAxisAlignment.start, 32 | children: [ 33 | Text( 34 | title, 35 | style: TextStyle(color: Color(0x99ffffff), fontSize: 14), 36 | ), 37 | SizedBox( 38 | height: 8, 39 | ), 40 | Text( 41 | desc, 42 | style: TextStyle(color: Color(0xffffffff), fontSize: 18), 43 | ) 44 | ], 45 | ), 46 | ); 47 | } 48 | 49 | Widget _buildBottomWidget() { 50 | const defaultStr = "--"; 51 | String apparentTemp = defaultStr; 52 | String humidity = defaultStr; 53 | String pressure = defaultStr; 54 | String wind = defaultStr; 55 | String pm25 = defaultStr; 56 | String ultraviolet = defaultStr; 57 | if (entity != null && 58 | entity.result != null && 59 | entity.result.realtime != null) { 60 | var realtime = entity.result.realtime; 61 | if (realtime.apparentTemperature != null) { 62 | apparentTemp = "${realtime.apparentTemperature.toInt()}°"; 63 | } 64 | if (realtime.humidity != null) { 65 | humidity = "${(realtime.humidity * 100).toInt()}%"; 66 | } 67 | if (realtime.pressure != null) { 68 | pressure = "${realtime.pressure}hPa"; 69 | } 70 | if (realtime.wind != null && realtime.wind.speed != null) { 71 | wind = "${realtime.wind.speed}km/h"; 72 | } 73 | if (realtime.airQuality != null && realtime.airQuality.pm25 != null) { 74 | pm25 = "${realtime.airQuality.pm25}"; 75 | } 76 | if (realtime.lifeIndex != null && 77 | realtime.lifeIndex.ultraviolet != null) { 78 | ultraviolet = "${realtime.lifeIndex.ultraviolet.index}"; 79 | } 80 | } 81 | return Container( 82 | padding: EdgeInsets.symmetric(horizontal: 30), 83 | child: Column( 84 | children: [ 85 | Row( 86 | children: [ 87 | _buildItem("体感", apparentTemp), 88 | _buildItem("湿度", humidity), 89 | ], 90 | ), 91 | Row( 92 | children: [ 93 | _buildItem("气压", pressure), 94 | _buildItem("风速", wind), 95 | ], 96 | ), 97 | Row( 98 | children: [ 99 | _buildItem("PM25", pm25), 100 | _buildItem("紫外线", ultraviolet), 101 | ], 102 | ), 103 | ], 104 | ), 105 | ); 106 | } 107 | 108 | @override 109 | Widget build(BuildContext context) { 110 | return Container( 111 | child: BlurRectWidget( 112 | child: Column( 113 | children: [ 114 | _buildSunSetRiseWidget(), 115 | _buildBottomWidget(), 116 | ], 117 | ), 118 | ), 119 | ); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/views/pages/home/speak_anim.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class SpeakAnim extends StatefulWidget { 6 | @override 7 | _SpeakAnimState createState() => _SpeakAnimState(); 8 | } 9 | 10 | class _SpeakAnimState extends State 11 | with SingleTickerProviderStateMixin { 12 | AnimationController _controller; 13 | Animation animation; 14 | 15 | @override 16 | void initState() { 17 | _controller = new AnimationController( 18 | vsync: this, duration: Duration(seconds: 6)); 19 | animation = Tween(begin: 0, end: 30).animate(_controller) 20 | ..addListener(() { 21 | setState(() {}); 22 | }); 23 | _controller.repeat(); 24 | super.initState(); 25 | } 26 | 27 | @override 28 | void deactivate() { 29 | _controller.stop(); 30 | super.deactivate(); 31 | } 32 | 33 | @override 34 | void dispose() { 35 | _controller.dispose(); 36 | super.dispose(); 37 | } 38 | 39 | @override 40 | Widget build(BuildContext context) { 41 | return Container( 42 | child: CustomPaint( 43 | painter: SpeakPainter(animation.value), 44 | size: Size(25, 15), 45 | ), 46 | ); 47 | } 48 | } 49 | 50 | class SpeakPainter extends CustomPainter { 51 | 52 | final double itemWidth = 3; 53 | Paint _paint = Paint(); 54 | final double value; 55 | 56 | SpeakPainter(this.value); 57 | 58 | @override 59 | void paint(Canvas canvas, Size size) { 60 | var width = size.width; 61 | var height = size.height; 62 | _paint.color = Colors.white; 63 | _paint.style = PaintingStyle.fill; 64 | _paint.isAntiAlias = true; 65 | double startX = -width; 66 | int count = width ~/ itemWidth; 67 | for (int i = 0; i < count; i++) { 68 | double x = startX + itemWidth * 2 * i + value; 69 | double y = (sin(x) + 1) * height / 3 + height / 3; 70 | canvas.drawRRect(RRect.fromLTRBAndCorners(x, height - y, x + itemWidth, height, topLeft: Radius.circular(4), topRight: Radius.circular(4)), _paint); 71 | } 72 | } 73 | 74 | @override 75 | bool shouldRepaint(CustomPainter oldDelegate) { 76 | return true; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lib/views/pages/search/hot_city_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_dynamic_weather/app/res/weather_type.dart'; 3 | import 'package:flutter_dynamic_weather/app/utils/Toast.dart'; 4 | import 'package:flutter_dynamic_weather/app/utils/color_utils.dart'; 5 | import 'package:flutter_dynamic_weather/app/utils/print_utils.dart'; 6 | import 'package:flutter_dynamic_weather/app/utils/shared_preference_util.dart'; 7 | import 'package:flutter_dynamic_weather/event/change_index_envent.dart'; 8 | import 'package:flutter_dynamic_weather/model/city_data.dart'; 9 | import 'package:flutter_dynamic_weather/model/city_model_entity.dart'; 10 | import 'package:flutter_dynamic_weather/views/app/flutter_app.dart'; 11 | import 'package:flutter_dynamic_weather/views/pages/search/search_app_bar.dart'; 12 | import 'package:flutter_dynamic_weather/views/pages/search/search_page.dart'; 13 | 14 | class HotCityView extends StatefulWidget { 15 | final CityItemClickCallback itemClickCallback; 16 | 17 | HotCityView({Key key, this.itemClickCallback}) : super(key: key); 18 | 19 | @override 20 | _HotCityViewState createState() => _HotCityViewState(); 21 | } 22 | 23 | class _HotCityViewState extends State { 24 | List _cityData; 25 | List cityModels; 26 | 27 | Widget _buildHotItem(BuildContext context, CityData cityData) { 28 | return Container( 29 | margin: EdgeInsets.only(left: 8, right: 8, top: 5, bottom: 5), 30 | child: ActionChip( 31 | padding: EdgeInsets.only(left: 16, right: 16, top: 10, bottom: 10), 32 | label: Text( 33 | "${cityData.name}", 34 | style: TextStyle(color: isAdd(cityData) ? Colors.blue : Colors.black), 35 | ), 36 | backgroundColor: ColorUtils.parse("#11000000"), 37 | onPressed: () async { 38 | if (isAdd(cityData)) { 39 | eventBus.fire(ChangeCityEvent(cityData.center)); 40 | Navigator.of(context).pop(); 41 | } else if (widget.itemClickCallback != null) { 42 | widget.itemClickCallback(cityData); 43 | } 44 | }, 45 | ), 46 | ); 47 | } 48 | 49 | bool isAdd(CityData cityData) { 50 | bool isAdd = false; 51 | if (cityModels != null && cityModels.isNotEmpty && cityData != null) { 52 | cityModels.forEach((element) { 53 | if (element.cityFlag == cityData.center) { 54 | isAdd = true; 55 | } 56 | }); 57 | } 58 | return isAdd; 59 | } 60 | 61 | Widget _buildHotView() { 62 | if (_cityData != null) { 63 | return Column( 64 | crossAxisAlignment: CrossAxisAlignment.start, 65 | children: [ 66 | Container( 67 | margin: EdgeInsets.only(left: 12), 68 | child: Text( 69 | "热门城市", 70 | style: TextStyle(color: ColorUtils.parse("#bb000000")), 71 | ), 72 | ), 73 | SizedBox( 74 | height: 10, 75 | ), 76 | Wrap( 77 | alignment: WrapAlignment.start, 78 | children: _cityData.map((e) => _buildHotItem(context, e)).toList(), 79 | ) 80 | ], 81 | ); 82 | } else { 83 | return null; 84 | } 85 | } 86 | 87 | Future fetchHotData() async { 88 | _cityData = [ 89 | CityData("北京市", "116.405285,39.904989"), 90 | CityData("上海市", "121.472644,31.231706"), 91 | CityData("广州市", "113.280637,23.125178"), 92 | CityData("深圳市", "114.085947,22.547"), 93 | CityData("珠海市", "113.553986,22.224979"), 94 | CityData("佛山市", "113.122717,23.028762"), 95 | CityData("南京市", "118.767413,32.041544"), 96 | CityData("苏州市", "120.619585,31.299379"), 97 | CityData("厦门市", "118.11022,24.490474"), 98 | CityData("南宁市", "108.320004,22.82402"), 99 | CityData("成都市", "104.065735,30.659462"), 100 | CityData("长沙市", "112.982279,28.19409"), 101 | CityData("杭州市", "120.153576,30.287459"), 102 | CityData("武汉市", "114.298572,30.584355"), 103 | CityData("青岛市", "120.355173,36.082982"), 104 | CityData("西安市", "125.151424,42.920415"), 105 | CityData("太原市", "112.549248,37.857014"), 106 | CityData("石家庄市", "114.502461,38.045474"), 107 | CityData("重庆市", "106.504962,29.533155"), 108 | CityData("天津市", "117.190182,39.125596"), 109 | ]; 110 | cityModels = await SPUtil.getCityModels(); 111 | setState(() {}); 112 | } 113 | 114 | @override 115 | void initState() { 116 | fetchHotData(); 117 | super.initState(); 118 | } 119 | 120 | @override 121 | Widget build(BuildContext context) { 122 | return Container( 123 | child: _buildHotView(), 124 | ); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /lib/views/pages/search/search_list_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_dynamic_weather/app/utils/print_utils.dart'; 3 | import 'package:flutter_dynamic_weather/event/change_index_envent.dart'; 4 | import 'package:flutter_dynamic_weather/model/city_data.dart'; 5 | import 'package:flutter_dynamic_weather/model/city_model_entity.dart'; 6 | import 'package:flutter_dynamic_weather/views/app/flutter_app.dart'; 7 | import 'package:flutter_dynamic_weather/views/pages/search/search_app_bar.dart'; 8 | import 'package:flutter_dynamic_weather/views/pages/search/search_page.dart'; 9 | import 'package:flutter_weather_bg/flutter_weather_bg.dart'; 10 | 11 | class SearchListView extends StatelessWidget { 12 | final List cityData; 13 | final CityItemClickCallback itemClickCallback; 14 | final List cityModels; 15 | 16 | SearchListView({Key key, this.cityData, this.itemClickCallback, this.cityModels}) : super(key: key); 17 | 18 | bool isAdd(CityData cityData) { 19 | bool isAdd = false; 20 | if (cityModels != null && cityModels.isNotEmpty && cityData != null) { 21 | cityModels.forEach((element) { 22 | if (element.cityFlag == cityData.center) { 23 | isAdd = true; 24 | } 25 | }); 26 | } 27 | return isAdd; 28 | } 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | if (cityData != null && cityData.isNotEmpty) { 33 | weatherPrint("SearchListView || ${cityData.length}"); 34 | var searchWidgetHeight = MediaQuery.of(context).size.height - 35 | kToolbarHeight - 36 | MediaQueryData.fromWindow(WidgetsBinding.instance.window) 37 | .padding 38 | .top - 10; 39 | return Container( 40 | height: searchWidgetHeight, 41 | margin: EdgeInsets.symmetric(horizontal: 10), 42 | child: ListView.separated( 43 | itemBuilder: (_, index) { 44 | return Card( 45 | elevation: 3, 46 | shape: RoundedRectangleBorder( 47 | borderRadius: BorderRadius.circular(20)), 48 | child: GestureDetector( 49 | child: Container( 50 | height: 80, 51 | decoration: BoxDecoration( 52 | borderRadius: BorderRadius.circular(20), 53 | gradient: LinearGradient( 54 | colors: WeatherUtil.getColor(WeatherType.sunny), 55 | stops: [0, 1], 56 | begin: Alignment.topCenter, 57 | end: Alignment.bottomCenter, 58 | )), 59 | child: Row( 60 | children: [ 61 | SizedBox(width: 20), 62 | Expanded( 63 | child: Text("${cityData[index].name}", style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold),), 64 | ), 65 | Text("${isAdd(cityData[index]) ? "已添加" : ""}", style: TextStyle(color: Colors.white, fontSize: 14),), 66 | SizedBox( 67 | width: 16, 68 | ) 69 | ], 70 | ), 71 | ), 72 | onTap: () { 73 | if (isAdd(cityData[index])) { 74 | eventBus.fire(ChangeCityEvent(cityData[index].center)); 75 | Navigator.of(context).pop(); 76 | } else if (itemClickCallback != null) { 77 | itemClickCallback(cityData[index]); 78 | } 79 | }, 80 | ), 81 | ); 82 | }, 83 | separatorBuilder: (_, index) => SizedBox(height: 10,), 84 | itemCount: cityData.length, 85 | physics: BouncingScrollPhysics(), 86 | ), 87 | ); 88 | } 89 | return Container(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_dynamic_weather 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 2.6.0+26 19 | 20 | environment: 21 | sdk: ">=2.8.0 <3.0.0" 22 | 23 | dependencies: 24 | flutter: 25 | sdk: flutter 26 | flutter_bloc: ^6.1.3 27 | equatable: ^2.0.0 28 | shared_preferences: ^2.0.3 29 | dio: ^3.0.10 30 | amap_location_fluttify: ^0.20.0 31 | location_permissions: ^3.0.0+1 32 | toast: ^0.1.5 33 | event_bus: ^2.0.0 34 | flutter_slidable: ^0.5.7 35 | umeng_analytics_plugin: ^1.0.3 36 | flutter_screenutil: ^4.0.3+3 37 | modal_bottom_sheet: ^2.0.0 38 | path_drawing: ^0.4.1+1 39 | url_launcher: ^5.5.1 40 | package_info: ^2.0.0 41 | ota_update: ^2.4.1 42 | flutter_tts: ^3.0.0 43 | flutter_weather_bg: ^2.8.2 44 | 45 | # The following adds the Cupertino Icons font to your application. 46 | # Use with the CupertinoIcons class for iOS style icons. 47 | cupertino_icons: ^1.0.2 48 | 49 | dev_dependencies: 50 | flutter_test: 51 | sdk: flutter 52 | 53 | # For information on the generic Dart part of this file, see the 54 | # following page: https://dart.dev/tools/pub/pubspec 55 | 56 | # The following section is specific to Flutter. 57 | flutter: 58 | 59 | # The following line ensures that the Material Icons font is 60 | # included with your application, so that you can use the icons in 61 | # the material Icons class. 62 | uses-material-design: true 63 | 64 | # To add assets to your application, add an assets section, like this: 65 | # assets: 66 | # - images/a_dot_burr.jpeg 67 | # - images/a_dot_ham.jpeg 68 | assets: 69 | - assets/images/ic_launcher.png 70 | 71 | - assets/images/carWashing.png 72 | - assets/images/coldRisk.png 73 | - assets/images/comfort.png 74 | - assets/images/dressing.png 75 | - assets/images/ultraviolet.png 76 | 77 | - assets/images/error.png 78 | - assets/images/empty.png 79 | - assets/images/caiyun.png 80 | - assets/images/typhoon.png 81 | - assets/images/play.png 82 | 83 | - assets/images/weather/ 84 | 85 | # An image asset can refer to one or more resolution-specific "variants", see 86 | # https://flutter.dev/assets-and-images/#resolution-aware. 87 | 88 | # For details regarding adding assets from package dependencies, see 89 | # https://flutter.dev/assets-and-images/#from-packages 90 | 91 | # To add custom fonts to your application, add a fonts section here, 92 | # in this "flutter" section. Each entry in this list should have a 93 | # "family" key with the font family name, and a "fonts" key with a 94 | # list giving the asset and other descriptors for the font. For 95 | # example: 96 | # fonts: 97 | # - family: Schyler 98 | # fonts: 99 | # - asset: fonts/Schyler-Regular.ttf 100 | # - asset: fonts/Schyler-Italic.ttf 101 | # style: italic 102 | # - family: Trajan Pro 103 | # fonts: 104 | # - asset: fonts/TrajanPro.ttf 105 | # - asset: fonts/TrajanPro_Bold.ttf 106 | # weight: 700 107 | # 108 | # For details regarding fonts from package dependencies, 109 | # see https://flutter.dev/custom-fonts/#from-packages 110 | -------------------------------------------------------------------------------- /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_dynamic_weather/views/app/flutter_app.dart'; 10 | import 'package:flutter_test/flutter_test.dart'; 11 | 12 | import 'package:flutter_dynamic_weather/main.dart'; 13 | 14 | void main() { 15 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 16 | // Build our app and trigger a frame. 17 | await tester.pumpWidget(FlutterApp()); 18 | 19 | // Verify that our counter starts at 0. 20 | expect(find.text('0'), findsOneWidget); 21 | expect(find.text('1'), findsNothing); 22 | 23 | // Tap the '+' icon and trigger a frame. 24 | await tester.tap(find.byIcon(Icons.add)); 25 | await tester.pump(); 26 | 27 | // Verify that our counter has incremented. 28 | expect(find.text('0'), findsNothing); 29 | expect(find.text('1'), findsOneWidget); 30 | }); 31 | } 32 | --------------------------------------------------------------------------------