├── res └── values │ └── strings_en.arb ├── ios ├── .symlinks │ ├── flutter │ └── plugins │ │ └── flutter_webview_plugin ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ └── project.pbxproj ├── Runner.xcworkspace │ └── contents.xcworkspacedata ├── Podfile.lock ├── .gitignore └── Podfile ├── zhihu.keystore ├── lib ├── images │ └── pavlova.jpg ├── presenter │ ├── mvp.dart │ ├── theme_list_presenter.dart │ └── theme_list_presenter_impl.dart ├── model │ ├── base_model.dart │ ├── ThemeModel.dart │ ├── homePageModel.dart │ └── theme_list_model.dart ├── common │ ├── common_loading_dialog.dart │ ├── common_divider.dart │ ├── common_snakeBar.dart │ ├── common_retry.dart │ └── constant.dart ├── repository │ ├── theme_list_repository.dart │ └── theme_list_repository_impl.dart ├── net │ ├── dio_factory.dart │ └── apis.dart ├── main.dart ├── exampleDemo │ ├── sampleApp1.dart │ ├── sampleApp2.dart │ ├── navigatorTest.dart │ ├── fadeAppTest.dart │ ├── listItem.dart │ ├── showLoading.dart │ ├── isolateApp.dart │ ├── platformChannel.dart │ ├── startApp.dart │ └── layoutApp.dart ├── utils │ └── RouterUtils.dart ├── generated │ └── i18n.dart ├── zhihu │ ├── storyItem.dart │ ├── zhihudaily.dart │ └── themeListPage.dart └── widget │ ├── homeBanner.dart │ └── drawerContent.dart ├── android ├── key.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── app │ ├── src │ │ └── main │ │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── layout │ │ │ │ └── test_activity.xml │ │ │ ├── values │ │ │ │ └── styles.xml │ │ │ └── drawable │ │ │ │ └── launch_background.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── yourcompany │ │ │ │ └── starter │ │ │ │ ├── TestActivity.java │ │ │ │ └── MainActivity.java │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── .gitignore ├── settings.gradle ├── build.gradle ├── gradlew.bat └── gradlew ├── .gitignore ├── README.md ├── .metadata ├── test └── widget_test.dart ├── pubspec.yaml ├── pubspec.lock └── flutter_01.log /res/values/strings_en.arb: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /ios/.symlinks/flutter: -------------------------------------------------------------------------------- 1 | /Users/lhw/flutter/bin/cache/artifacts/engine -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /zhihu.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/zhihu.keystore -------------------------------------------------------------------------------- /lib/images/pavlova.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/lib/images/pavlova.jpg -------------------------------------------------------------------------------- /ios/.symlinks/plugins/flutter_webview_plugin: -------------------------------------------------------------------------------- 1 | /Users/lhw/.pub-cache/hosted/pub.dartlang.org/flutter_webview_plugin-0.1.6 -------------------------------------------------------------------------------- /android/key.properties: -------------------------------------------------------------------------------- 1 | storePassword=rice1985 2 | keyPassword=rice1985 3 | keyAlias=key 4 | storeFile=/Users/lhw/key.jks 5 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /lib/presenter/mvp.dart: -------------------------------------------------------------------------------- 1 | 2 | abstract class IView { 3 | setPresenter(T presenter); 4 | } 5 | 6 | abstract class IPresenter{ 7 | init(); 8 | } 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .atom/ 3 | .dart_tool/ 4 | .idea 5 | .vscode/ 6 | .packages 7 | .pub/ 8 | build/ 9 | ios/.generated/ 10 | packages 11 | .flutter-plugins 12 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.class 3 | .gradle 4 | /local.properties 5 | /.idea/workspace.xml 6 | /.idea/libraries 7 | .DS_Store 8 | /build 9 | /captures 10 | GeneratedPluginRegistrant.java 11 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MelonRice/ZhihuDailyFlutter/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zhihudaily_flutter 2 | 3 | A new Flutter zhihudaily project. 4 | 5 | ## Getting Started 6 | 7 | For help getting started with Flutter, view our online 8 | [documentation](https://flutter.io/). 9 | 10 | waiting.. 11 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/model/base_model.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | class BaseModel{ 5 | 6 | final int code; 7 | 8 | final String errorMsg; 9 | 10 | final T data; 11 | 12 | const BaseModel({this.code, this.errorMsg, this.data}); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /lib/common/common_loading_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ProgressDialog{ 4 | 5 | static Widget buildProgressDialog() { 6 | return new Center(child: new CircularProgressIndicator()); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /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-4.1-all.zip 7 | -------------------------------------------------------------------------------- /lib/common/common_divider.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class CommonDivider { 4 | static Widget buildDivider() { 5 | return new Padding( 6 | padding: const EdgeInsets.only(left: 12.0, right: 12.0), 7 | child: new Divider(height: 1.0), 8 | ); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.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: eef77ccaff6ca0a9884c7454938b7833746e95df 8 | channel: dev 9 | -------------------------------------------------------------------------------- /lib/repository/theme_list_repository.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:zhihudaily/model/theme_list_model.dart'; 4 | import 'package:zhihudaily/model/base_model.dart'; 5 | 6 | abstract class ThemeListRepository { 7 | 8 | Future> loadThemeList(String themeId,String lastId); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/layout/test_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /android/app/src/main/java/com/yourcompany/starter/TestActivity.java: -------------------------------------------------------------------------------- 1 | package com.yourcompany.starter; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | public class TestActivity extends Activity { 7 | @Override 8 | public void onCreate(Bundle savedInstanceState) { 9 | super.onCreate(savedInstanceState); 10 | setContentView(R.layout.test_activity); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/presenter/theme_list_presenter.dart: -------------------------------------------------------------------------------- 1 | import 'package:zhihudaily/model/base_model.dart'; 2 | import 'package:zhihudaily/model/theme_list_model.dart'; 3 | import 'package:zhihudaily/presenter/mvp.dart'; 4 | 5 | abstract class ThemeListPresenter implements IPresenter { 6 | loadThemeList(String themeId, String lastId); 7 | } 8 | 9 | abstract class ThemeListView implements IView { 10 | void onLoadThemeListSuc(BaseModel model); 11 | void onLoadThemeListFail(); 12 | } 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /lib/model/ThemeModel.dart: -------------------------------------------------------------------------------- 1 | class ThemeModel { 2 | final String thumbnail; 3 | final String description; 4 | final int id; 5 | final String name; 6 | final int color; 7 | 8 | const ThemeModel( 9 | {this.thumbnail, this.description, this.id, this.name, this.color}); 10 | 11 | ThemeModel.fromJson(Map json) 12 | : thumbnail = json['thumbnail'], 13 | description = json['description'], 14 | id = json['id'], 15 | color = json['color'], 16 | name = json['name']; 17 | } 18 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /lib/common/common_snakeBar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | 4 | class CommonSnakeBar{ 5 | 6 | static buildSnakeBar(BuildContext context,String str) { 7 | final snackBar = new SnackBar(content: new Text(str)); 8 | Scaffold.of(context).showSnackBar(snackBar); 9 | } 10 | 11 | //如果弹不出请用这个 12 | static buildSnakeBarByKey(final GlobalKey key,BuildContext context,String str) { 13 | final snackBar = new SnackBar(content: new Text(str)); 14 | key.currentState.showSnackBar(snackBar); 15 | } 16 | 17 | } 18 | 19 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.0.1' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /lib/net/dio_factory.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | 3 | //总觉得怪怪的,但是打印出来确实是只有一个dio对象。。 4 | 5 | class DioFactory { 6 | static Dio _dio; 7 | 8 | static DioFactory _instance; 9 | 10 | static DioFactory getInstance() { 11 | if (_instance == null) { 12 | _instance = new DioFactory._(); 13 | _instance._init(); 14 | } 15 | return _instance; 16 | } 17 | 18 | DioFactory._(); 19 | 20 | _init(){ 21 | _dio = new Dio(); 22 | } 23 | 24 | getDio() { 25 | return _dio; 26 | } 27 | } 28 | 29 | //测试是否是单例 30 | void main() { 31 | print(DioFactory.getInstance().getDio() == DioFactory.getInstance().getDio()); 32 | } 33 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - flutter_webview_plugin (0.0.1): 4 | - Flutter 5 | 6 | DEPENDENCIES: 7 | - Flutter (from `.symlinks/flutter/ios`) 8 | - flutter_webview_plugin (from `.symlinks/plugins/flutter_webview_plugin/ios`) 9 | 10 | EXTERNAL SOURCES: 11 | Flutter: 12 | :path: ".symlinks/flutter/ios" 13 | flutter_webview_plugin: 14 | :path: ".symlinks/plugins/flutter_webview_plugin/ios" 15 | 16 | SPEC CHECKSUMS: 17 | Flutter: 9d0fac939486c9aba2809b7982dfdbb47a7b0296 18 | flutter_webview_plugin: 116575b48572029304775b768e9f15ebfc316274 19 | 20 | PODFILE CHECKSUM: 7765ea4305eaab0b3dfd384c7de11902aa3195fd 21 | 22 | COCOAPODS: 1.5.3 23 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | *.pbxuser 16 | *.mode1v3 17 | *.mode2v3 18 | *.perspectivev3 19 | 20 | !default.pbxuser 21 | !default.mode1v3 22 | !default.mode2v3 23 | !default.perspectivev3 24 | 25 | xcuserdata 26 | 27 | *.moved-aside 28 | 29 | *.pyc 30 | *sync/ 31 | Icon? 32 | .tags* 33 | 34 | /Flutter/app.flx 35 | /Flutter/app.zip 36 | /Flutter/flutter_assets/ 37 | /Flutter/App.framework 38 | /Flutter/Flutter.framework 39 | /Flutter/Generated.xcconfig 40 | /ServiceDefinitions.json 41 | 42 | Pods/ 43 | -------------------------------------------------------------------------------- /lib/presenter/theme_list_presenter_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:zhihudaily/presenter/theme_list_presenter.dart'; 2 | import 'package:zhihudaily/repository/theme_list_repository.dart'; 3 | import 'package:zhihudaily/repository/theme_list_repository_impl.dart'; 4 | 5 | class ThemeListPresenterImpl implements ThemeListPresenter { 6 | 7 | ThemeListView _view; 8 | 9 | ThemeListRepository _repository; 10 | 11 | ThemeListPresenterImpl(this._view) { 12 | _view.setPresenter(this); 13 | } 14 | 15 | 16 | @override 17 | loadThemeList(String themeId, String lastId) { 18 | assert(_view != null); 19 | _repository.loadThemeList(themeId,lastId).then((data) { 20 | _view.onLoadThemeListSuc(data); 21 | }).catchError((error) { 22 | _view.onLoadThemeListFail(); 23 | }); 24 | } 25 | 26 | @override 27 | init() { 28 | _repository = new ThemeListRepositoryImpl(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:zhihudaily/exampleDemo/sampleApp2.dart'; 3 | import 'package:zhihudaily/exampleDemo/sampleApp1.dart'; 4 | import 'package:zhihudaily/exampleDemo/fadeAppTest.dart'; 5 | import 'package:zhihudaily/exampleDemo/navigatorTest.dart'; 6 | import 'package:zhihudaily/exampleDemo/platformChannel.dart'; 7 | import 'package:zhihudaily/zhihu/zhihudaily.dart'; 8 | import 'package:zhihudaily/exampleDemo/showLoading.dart'; 9 | import 'package:zhihudaily/exampleDemo/isolateApp.dart'; 10 | import 'package:zhihudaily/exampleDemo/listItem.dart'; 11 | import 'package:zhihudaily/exampleDemo/layoutApp.dart'; 12 | import 'package:zhihudaily/exampleDemo/startApp.dart'; 13 | import 'package:flutter/rendering.dart' show debugPaintSizeEnabled; 14 | 15 | //void main() => runApp(new LayoutApp()); 16 | 17 | void main() { 18 | debugPaintSizeEnabled = true; 19 | runApp(StartApp()); 20 | } 21 | -------------------------------------------------------------------------------- /lib/net/apis.dart: -------------------------------------------------------------------------------- 1 | class Apis { 2 | 3 | //首页数据 4 | static const String latest = "news/latest"; 5 | 6 | //首页数据 跟日期 example:news/before/20131119 7 | static const String before ="news/before/" ; 8 | 9 | //详情 跟id example:news/3892357 10 | static const String detail = "news/"; 11 | 12 | //评论数 点赞数 跟id example:news/3892357 13 | static const String story_extra = "story-extra/"; 14 | 15 | //长评论详情 跟id example:story/8997528/long-comments 16 | static const String long_comment = "story/id/long-comments"; 17 | 18 | //短评论详情 跟id 19 | static const String short_comment = "story/id/short-comments"; 20 | 21 | //查看主题日报分类列表 22 | static const String themes = "themes"; 23 | 24 | //查看某个主题的列表 跟id example:themes/13 25 | static const String themes_list = "theme/"; 26 | 27 | //查看某个主题的列表 跟tid story_id 是每次请求的最后一条 example:theme/13/before/4731018 28 | static const String themes_list_before = "/before/"; 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | UIRequiredDeviceCapabilities 24 | 25 | arm64 26 | 27 | MinimumOSVersion 28 | 8.0 29 | 30 | 31 | -------------------------------------------------------------------------------- /lib/model/homePageModel.dart: -------------------------------------------------------------------------------- 1 | class HomePageModel { 2 | static const int itemTypeNormal = 0; 3 | static const int itemTypeBanner = 1; 4 | 5 | final List images; 6 | final int type; 7 | final int id; 8 | final String title; 9 | 10 | int itemType = itemTypeNormal; 11 | 12 | HomePageModel({this.images, this.type, this.id, this.title}); 13 | 14 | setItemType(int type) { 15 | itemType = type; 16 | } 17 | 18 | HomePageModel.fromJson(Map json) 19 | : images = json['images'], 20 | type = json['type'], 21 | id = json['id'], 22 | title = json['title']; 23 | } 24 | 25 | class TopStoriesModel { 26 | final String image; 27 | final int type; 28 | final int id; 29 | final String title; 30 | 31 | const TopStoriesModel({this.image, this.type, this.id, this.title}); 32 | 33 | TopStoriesModel.fromJson(Map json) 34 | : image = json['image'], 35 | type = json['type'], 36 | id = json['id'], 37 | title = json['title']; 38 | } 39 | -------------------------------------------------------------------------------- /lib/common/common_retry.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class CommonRetry { 4 | static Widget buildRetry(VoidCallback v) { 5 | return new Center( 6 | child: new Padding( 7 | padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0), 8 | child: new InkWell( 9 | borderRadius: new BorderRadius.all(new Radius.circular(10.0)), 10 | onTap: v, 11 | child: new Container( 12 | width: 200.0, 13 | alignment: Alignment.center, 14 | padding: const EdgeInsets.only( 15 | left: 10.0, top: 10.0, right: 10.0, bottom: 10.0), 16 | height: 48.0, 17 | decoration: new BoxDecoration( 18 | border: new Border.all( 19 | width: 1.0, 20 | color: Colors.blue, 21 | ), 22 | borderRadius: new BorderRadius.all(new Radius.circular(10.0)), 23 | ), 24 | child: new Text('网络异常,请检查后重试'), 25 | ), 26 | )), 27 | ); 28 | } 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter 3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to 4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties 5 | // are correct. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_test/flutter_test.dart'; 9 | import 'package:zhihudaily/exampleDemo/sampleApp1.dart'; 10 | import 'package:zhihudaily/main.dart'; 11 | 12 | void main() { 13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(new SampleApp1()); 16 | 17 | // Verify that our counter starts at 0. 18 | expect(find.text('0'), findsOneWidget); 19 | expect(find.text('1'), findsNothing); 20 | 21 | // Tap the '+' icon and trigger a frame. 22 | await tester.tap(find.byIcon(Icons.add)); 23 | await tester.pump(); 24 | 25 | // Verify that our counter has incremented. 26 | expect(find.text('0'), findsNothing); 27 | expect(find.text('1'), findsOneWidget); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /lib/exampleDemo/sampleApp1.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SampleApp1 extends StatelessWidget { 4 | // This widget is the root of your application. 5 | @override 6 | Widget build(BuildContext context) { 7 | return new MaterialApp( 8 | title: 'Sample App', 9 | theme: new ThemeData( 10 | primarySwatch: Colors.blue, 11 | ), 12 | home: new SampleAppPage(), 13 | ); 14 | } 15 | } 16 | 17 | class SampleAppPage extends StatefulWidget { 18 | SampleAppPage({Key key}) : super(key: key); 19 | 20 | @override 21 | _SampleAppPageState createState() => new _SampleAppPageState(); 22 | } 23 | 24 | class _SampleAppPageState extends State { 25 | // Default placeholder text 26 | String textToShow = "I Like Flutter"; 27 | 28 | void _updateText() { 29 | setState(() { 30 | // update the text 31 | textToShow = "Flutter is Awesome!"; 32 | }); 33 | } 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | return new Scaffold( 38 | appBar: new AppBar( 39 | title: new Text("Sample App"), 40 | ), 41 | body: new Center(child: new Text(textToShow)), 42 | floatingActionButton: new FloatingActionButton( 43 | onPressed: _updateText, 44 | tooltip: 'Update Text', 45 | child: new Icon(Icons.update), 46 | ), 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/common/constant.dart: -------------------------------------------------------------------------------- 1 | 2 | class Constant { 3 | 4 | static const String baseUrl="https://news-at.zhihu.com/api/4/"; 5 | 6 | //宽高常量 7 | static const double bannerHeight = 200.0; 8 | static const double normalItemHeight = 100.0; 9 | static const double dateTimeItemHeight = 40.0; 10 | 11 | //字符串常量 12 | static const String todayHot = '今日热点'; 13 | static const String themeTitle = '专题'; 14 | static const String storyTitle = '详情'; 15 | static const String tips = '本页应该由banner+html组成,由于Flutter对Html支持的问题,以及暂时没找到好的解决方案,暂缓该功能,怕忘记了,故保留该页面,作为优化\n请点击下面链接跳转到webview查看本文'; 16 | 17 | 18 | //SharedPreferences key 19 | static const String spThemeCache = 'sp_theme_cache'; 20 | static const String spThemeCacheHours = 'sp_theme_cache_hours'; 21 | 22 | 23 | //time 24 | static const int oneDay = 24 * 60 * 60; 25 | 26 | //def headimg 27 | static const String defHeadimg = 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRb0V6OlKdbsP-45kue1bb3QsVF2vV6Ncm_Nw3OzSwdTmWstfzY'; 28 | 29 | //comment pop 30 | static const String popReply = '回复'; 31 | static const String popAgree = '赞同'; 32 | static const String popCopy = '复制'; 33 | static const String popReport = '举报'; 34 | 35 | //def bg 36 | static const String defBg ='https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1529724791909&di=02909c10c9d8ed553d070522f4c11b44&imgtype=0&src=http%3A%2F%2Fimg17.3lian.com%2Fd%2Ffile%2F201702%2F14%2Fbf13787b4f6a5c346c07b8f10466a682.jpg'; 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /lib/exampleDemo/sampleApp2.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SampleApp2 extends StatelessWidget { 4 | // This widget is the root of your application. 5 | @override 6 | Widget build(BuildContext context) { 7 | return new MaterialApp( 8 | title: 'Sample App', 9 | theme: new ThemeData( 10 | primarySwatch: Colors.blue, 11 | ), 12 | home: new SampleAppPage(), 13 | ); 14 | } 15 | } 16 | 17 | class SampleAppPage extends StatefulWidget { 18 | SampleAppPage({Key key}) : super(key: key); 19 | 20 | @override 21 | _SampleAppPageState createState() => new _SampleAppPageState(); 22 | } 23 | 24 | class _SampleAppPageState extends State { 25 | // Default value for toggle 26 | bool toggle = true; 27 | void _toggle() { 28 | setState(() { 29 | toggle = !toggle; 30 | }); 31 | } 32 | 33 | _getToggleChild() { 34 | if (toggle) { 35 | return new Text('Toggle One'); 36 | } else { 37 | return new MaterialButton(onPressed: () {}, child: new Text('Toggle Two')); 38 | } 39 | } 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | return new Scaffold( 44 | appBar: new AppBar( 45 | title: new Text("Sample App"), 46 | ), 47 | body: new Center( 48 | child: _getToggleChild(), 49 | ), 50 | floatingActionButton: new FloatingActionButton( 51 | onPressed: _toggle, 52 | tooltip: 'Update Text', 53 | child: new Icon(Icons.update), 54 | ), 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/model/theme_list_model.dart: -------------------------------------------------------------------------------- 1 | class ThemeListModel { 2 | final String description; 3 | 4 | final String name; 5 | 6 | final String background; 7 | 8 | final String image; 9 | 10 | final List stories; 11 | 12 | final List editors; 13 | 14 | const ThemeListModel( 15 | {this.description, 16 | this.name, 17 | this.background, 18 | this.image, 19 | this.stories, 20 | this.editors}); 21 | } 22 | 23 | class ThemeListStoriesModel { 24 | 25 | static const int itemTypeNormal = 0; 26 | static const int itemTypeBanner = 1; 27 | static const int itemTypeEditor = 2; 28 | 29 | final List images; 30 | final int type; 31 | final int id; 32 | final String title; 33 | 34 | int itemType = itemTypeNormal; 35 | 36 | setItemType(int type) { 37 | itemType = type; 38 | } 39 | 40 | ThemeListStoriesModel({this.images, this.type, this.id, this.title}); 41 | 42 | ThemeListStoriesModel.fromJson(Map json) 43 | : images = json['images'], 44 | type = json['type'], 45 | id = json['id'], 46 | title = json['title']; 47 | } 48 | 49 | class ThemeListEditorsModel { 50 | final String url; 51 | final String bio; 52 | final String avatar; 53 | final int id; 54 | final String name; 55 | 56 | const ThemeListEditorsModel( 57 | {this.url, this.bio, this.avatar, this.id, this.name}); 58 | 59 | ThemeListEditorsModel.fromJson(Map json) 60 | : url = json['url'], 61 | bio = json['bio'], 62 | id = json['id'], 63 | name = json['name'], 64 | avatar = json['avatar']; 65 | } 66 | -------------------------------------------------------------------------------- /lib/exampleDemo/navigatorTest.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class NavigatorTest extends StatelessWidget { 4 | // This widget is the root of your application. 5 | @override 6 | Widget build(BuildContext context) { 7 | return new MaterialApp( 8 | title: 'Fade Demo', 9 | theme: new ThemeData( 10 | primarySwatch: Colors.blue, 11 | ), 12 | home: new MyFadeTest(title: 'Navigator Demo'), 13 | routes: { 14 | '/a': (BuildContext context) => new MyFadeTest(title: 'page A'), 15 | }, 16 | ); 17 | } 18 | } 19 | class MyFadeTest extends StatefulWidget { 20 | MyFadeTest({Key key, this.title}) : super(key: key); 21 | final String title; 22 | @override 23 | _MyFadeTest createState() => new _MyFadeTest(); 24 | } 25 | 26 | class _MyFadeTest extends State with TickerProviderStateMixin { 27 | AnimationController controller; 28 | CurvedAnimation curve; 29 | 30 | @override 31 | void initState() { 32 | controller = new AnimationController(duration: const Duration(milliseconds: 2000), vsync: this); 33 | curve = new CurvedAnimation(parent: controller, curve: Curves.easeIn); 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return new Scaffold( 39 | appBar: new AppBar( 40 | title: new Text(widget.title), 41 | ), 42 | body: new Center( 43 | ), 44 | floatingActionButton: new FloatingActionButton( 45 | tooltip: 'Fade', 46 | child: new Icon(Icons.arrow_forward_ios), 47 | onPressed: () { 48 | Navigator.of(context).pushNamed('/a'); 49 | }, 50 | ), 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/exampleDemo/fadeAppTest.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class FadeAppTest extends StatelessWidget { 4 | // This widget is the root of your application. 5 | @override 6 | Widget build(BuildContext context) { 7 | return new MaterialApp( 8 | title: 'Fade Demo', 9 | theme: new ThemeData( 10 | primarySwatch: Colors.blue, 11 | ), 12 | home: new MyFadeTest(title: 'Fade Demo'), 13 | ); 14 | } 15 | } 16 | 17 | class MyFadeTest extends StatefulWidget { 18 | MyFadeTest({Key key, this.title}) : super(key: key); 19 | final String title; 20 | @override 21 | _MyFadeTest createState() => new _MyFadeTest(); 22 | } 23 | 24 | class _MyFadeTest extends State with TickerProviderStateMixin { 25 | AnimationController controller; 26 | CurvedAnimation curve; 27 | 28 | @override 29 | void initState() { 30 | controller = new AnimationController(duration: const Duration(milliseconds: 2000), vsync: this); 31 | curve = new CurvedAnimation(parent: controller, curve: Curves.easeIn); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | return new Scaffold( 37 | appBar: new AppBar( 38 | title: new Text(widget.title), 39 | ), 40 | body: new Center( 41 | child: new Container( 42 | child: new FadeTransition( 43 | opacity: curve, 44 | child: new FlutterLogo( 45 | size: 100.0, 46 | )))), 47 | floatingActionButton: new FloatingActionButton( 48 | tooltip: 'Fade', 49 | child: new Icon(Icons.brush), 50 | onPressed: () { 51 | controller.forward(); 52 | }, 53 | ), 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | zhihudaily 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | arm64 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UIViewControllerBasedStatusBarAppearance 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /lib/exampleDemo/listItem.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | 5 | class ListItemApp extends StatelessWidget { 6 | final List items; 7 | 8 | ListItemApp({Key key, @required this.items}) : super(key: key); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | final title = 'Mixed List'; 13 | 14 | return new MaterialApp( 15 | title: title, 16 | home: new Scaffold( 17 | appBar: new AppBar( 18 | title: new Text(title), 19 | ), 20 | body: new ListView.builder( 21 | // Let the ListView know how many items it needs to build 22 | itemCount: items.length, 23 | // Provide a builder function. This is where the magic happens! We'll 24 | // convert each item into a Widget based on the type of item it is. 25 | itemBuilder: (context, index) { 26 | final item = items[index]; 27 | 28 | if (item is HeadingItem) { 29 | return new ListTile( 30 | title: new Text( 31 | item.heading, 32 | style: Theme.of(context).textTheme.headline, 33 | ), 34 | ); 35 | } else if (item is MessageItem) { 36 | return new ListTile( 37 | title: new Text(item.sender), 38 | subtitle: new Text(item.body), 39 | ); 40 | } 41 | }, 42 | ), 43 | ), 44 | ); 45 | } 46 | } 47 | 48 | // The base class for the different types of items the List can contain 49 | abstract class ListItem {} 50 | 51 | // A ListItem that contains data to display a heading 52 | class HeadingItem implements ListItem { 53 | final String heading; 54 | 55 | HeadingItem(this.heading); 56 | } 57 | 58 | // A ListItem that contains data to display a message 59 | class MessageItem implements ListItem { 60 | final String sender; 61 | final String body; 62 | 63 | MessageItem(this.sender, this.body); 64 | } 65 | -------------------------------------------------------------------------------- /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 | apply plugin: 'com.android.application' 15 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 16 | 17 | def keystorePropertiesFile = rootProject.file("key.properties") 18 | def keystoreProperties = new Properties() 19 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 20 | 21 | android { 22 | compileSdkVersion 27 23 | 24 | lintOptions { 25 | disable 'InvalidPackage' 26 | } 27 | 28 | defaultConfig { 29 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 30 | applicationId "com.yourcompany.starter" 31 | minSdkVersion 16 32 | targetSdkVersion 27 33 | versionCode 1 34 | versionName "1.0" 35 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 36 | } 37 | 38 | signingConfigs { 39 | release { 40 | keyAlias keystoreProperties['keyAlias'] 41 | keyPassword keystoreProperties['keyPassword'] 42 | storeFile file(keystoreProperties['storeFile']) 43 | storePassword keystoreProperties['storePassword'] 44 | } 45 | } 46 | buildTypes { 47 | release { 48 | signingConfig signingConfigs.release 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | testImplementation 'junit:junit:4.12' 59 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 60 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 61 | } 62 | -------------------------------------------------------------------------------- /lib/exampleDemo/showLoading.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:http/http.dart' as http; 5 | 6 | class ShowLoading extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return new MaterialApp( 10 | title: 'Sample App', 11 | theme: new ThemeData( 12 | primarySwatch: Colors.blue, 13 | ), 14 | home: new SampleAppPage(), 15 | ); 16 | } 17 | } 18 | 19 | class SampleAppPage extends StatefulWidget { 20 | SampleAppPage({Key key}) : super(key: key); 21 | 22 | @override 23 | _SampleAppPageState createState() => new _SampleAppPageState(); 24 | } 25 | 26 | class _SampleAppPageState extends State { 27 | List widgets = []; 28 | 29 | @override 30 | void initState() { 31 | super.initState(); 32 | loadData(); 33 | } 34 | 35 | showLoadingDialog() { 36 | if (widgets.length == 0) { 37 | return true; 38 | } 39 | 40 | return false; 41 | } 42 | 43 | getBody() { 44 | if (showLoadingDialog()) { 45 | return getProgressDialog(); 46 | } else { 47 | return getListView(); 48 | } 49 | } 50 | 51 | getProgressDialog() { 52 | return new Center(child: new CircularProgressIndicator()); 53 | } 54 | 55 | @override 56 | Widget build(BuildContext context) { 57 | return new Scaffold( 58 | appBar: new AppBar( 59 | title: new Text("Sample App"), 60 | ), 61 | body: getBody()); 62 | } 63 | 64 | ListView getListView() => new ListView.builder( 65 | itemCount: widgets.length, 66 | itemBuilder: (BuildContext context, int position) { 67 | return getRow(position); 68 | }); 69 | 70 | Widget getRow(int i) { 71 | return new Padding(padding: new EdgeInsets.all(10.0), child: new Text("Row ${widgets[i]["title"]}")); 72 | } 73 | 74 | loadData() async { 75 | String dataURL = "https://jsonplaceholder.typicode.com/posts"; 76 | http.Response response = await http.get(dataURL); 77 | setState(() { 78 | widgets = JSON.decode(response.body); 79 | }); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 15 | 19 | 26 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: zhihudaily 2 | description: A new Flutter project. 3 | 4 | dependencies: 5 | flutter: 6 | sdk: flutter 7 | 8 | flutter_webview_plugin: "^0.3.0+2" 9 | transparent_image: "^0.1.0" 10 | dio: "^1.0.9" 11 | http: "^0.12.0" 12 | 13 | # The following adds the Cupertino Icons font to your application. 14 | # Use with the CupertinoIcons class for iOS style icons. 15 | cupertino_icons: ^0.1.2 16 | english_words: ^3.1.5 17 | 18 | dev_dependencies: 19 | flutter_test: 20 | sdk: flutter 21 | 22 | 23 | # For information on the generic Dart part of this file, see the 24 | # following page: https://www.dartlang.org/tools/pub/pubspec 25 | 26 | # The following section is specific to Flutter. 27 | flutter: 28 | 29 | # The following line ensures that the Material Icons font is 30 | # included with your application, so that you can use the icons in 31 | # the material Icons class. 32 | uses-material-design: true 33 | assets: 34 | - lib/images/pavlova.jpg 35 | 36 | # To add assets to your application, add an assets section, like this: 37 | # assets: 38 | # - images/a_dot_burr.jpeg 39 | # - images/a_dot_ham.jpeg 40 | 41 | # An image asset can refer to one or more resolution-specific "variants", see 42 | # https://flutter.io/assets-and-images/#resolution-aware. 43 | 44 | # For details regarding adding assets from package dependencies, see 45 | # https://flutter.io/assets-and-images/#from-packages 46 | 47 | # To add custom fonts to your application, add a fonts section here, 48 | # in this "flutter" section. Each entry in this list should have a 49 | # "family" key with the font family name, and a "fonts" key with a 50 | # list giving the asset and other descriptors for the font. For 51 | # example: 52 | # fonts: 53 | # - family: Schyler 54 | # fonts: 55 | # - asset: fonts/Schyler-Regular.ttf 56 | # - asset: fonts/Schyler-Italic.ttf 57 | # style: italic 58 | # - family: Trajan Pro 59 | # fonts: 60 | # - asset: fonts/TrajanPro.ttf 61 | # - asset: fonts/TrajanPro_Bold.ttf 62 | # weight: 700 63 | # 64 | # For details regarding fonts from package dependencies, 65 | # see https://flutter.io/custom-fonts/#from-packages 66 | -------------------------------------------------------------------------------- /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 | def parse_KV_file(file, separator='=') 8 | file_abs_path = File.expand_path(file) 9 | if !File.exists? file_abs_path 10 | return []; 11 | end 12 | pods_ary = [] 13 | skip_line_start_symbols = ["#", "/"] 14 | File.foreach(file_abs_path) { |line| 15 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 16 | plugin = line.split(pattern=separator) 17 | if plugin.length == 2 18 | podname = plugin[0].strip() 19 | path = plugin[1].strip() 20 | podpath = File.expand_path("#{path}", file_abs_path) 21 | pods_ary.push({:name => podname, :path => podpath}); 22 | else 23 | puts "Invalid plugin specification: #{line}" 24 | end 25 | } 26 | return pods_ary 27 | end 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | 32 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 33 | # referring to absolute paths on developers' machines. 34 | system('rm -rf .symlinks') 35 | system('mkdir -p .symlinks/plugins') 36 | 37 | # Flutter Pods 38 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') 39 | if generated_xcode_build_settings.empty? 40 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." 41 | end 42 | generated_xcode_build_settings.map { |p| 43 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR' 44 | symlink = File.join('.symlinks', 'flutter') 45 | File.symlink(File.dirname(p[:path]), symlink) 46 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) 47 | end 48 | } 49 | 50 | # Plugin Pods 51 | plugin_pods = parse_KV_file('../.flutter-plugins') 52 | plugin_pods.map { |p| 53 | symlink = File.join('.symlinks', 'plugins', p[:name]) 54 | File.symlink(p[:path], symlink) 55 | pod p[:name], :path => File.join(symlink, 'ios') 56 | } 57 | end 58 | 59 | post_install do |installer| 60 | installer.pods_project.targets.each do |target| 61 | target.build_configurations.each do |config| 62 | config.build_settings['ENABLE_BITCODE'] = 'NO' 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /lib/utils/RouterUtils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 5 | import 'package:http/http.dart' as http; 6 | import 'package:zhihudaily/zhihu/themeListPage.dart'; 7 | import 'package:zhihudaily/zhihu/zhihudaily.dart'; 8 | 9 | class RouterUtils { 10 | 11 | static routeToMain(BuildContext context) { 12 | Navigator.of(context).push(new PageRouteBuilder( 13 | opaque: false, 14 | pageBuilder: (BuildContext context, _, __) { 15 | return new ZhihuDailyApp(); 16 | }, 17 | transitionsBuilder: (_, Animation animation, __, Widget child) { 18 | return new FadeTransition( 19 | opacity: animation, 20 | child: new FadeTransition( 21 | opacity: 22 | new Tween(begin: 0.5, end: 1.0).animate(animation), 23 | child: child, 24 | ), 25 | ); 26 | })); 27 | } 28 | 29 | static startWebView(BuildContext context, int id) async { 30 | String dataURL = "https://news-at.zhihu.com/api/4/news/$id"; 31 | http.Response response = await http.get(dataURL); 32 | 33 | Navigator.of(context).push(new PageRouteBuilder( 34 | opaque: false, 35 | pageBuilder: (BuildContext context, _, __) { 36 | return new WebviewScaffold( 37 | url: json.decode(response.body)["share_url"], 38 | appBar: new AppBar( 39 | title: new Text(json.decode(response.body)["title"]), 40 | ), 41 | withZoom: true, 42 | withLocalStorage: true, 43 | ); 44 | }, 45 | transitionsBuilder: (_, Animation animation, __, Widget child) { 46 | return new FadeTransition( 47 | opacity: animation, 48 | child: new FadeTransition( 49 | opacity: 50 | new Tween(begin: 0.5, end: 1.0).animate(animation), 51 | child: child, 52 | ), 53 | ); 54 | })); 55 | 56 | } 57 | 58 | static route2ThemeList(BuildContext context, String themeId) { 59 | Navigator.of(context).push(new PageRouteBuilder( 60 | opaque: false, 61 | pageBuilder: (BuildContext context, _, __) { 62 | return new ThemeListPage(themeId); 63 | }, 64 | transitionsBuilder: (_, Animation animation, __, Widget child) { 65 | return new FadeTransition( 66 | opacity: animation, 67 | child: new FadeTransition( 68 | opacity: 69 | new Tween(begin: 0.5, end: 1.0).animate(animation), 70 | child: child, 71 | ), 72 | ); 73 | })); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/repository/theme_list_repository_impl.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:zhihudaily/common/constant.dart'; 5 | import 'package:zhihudaily/model/base_model.dart'; 6 | import 'package:zhihudaily/model/theme_list_model.dart'; 7 | import 'package:zhihudaily/repository/theme_list_repository.dart'; 8 | import 'package:zhihudaily/net/apis.dart'; 9 | import 'package:zhihudaily/net/dio_factory.dart'; 10 | import 'package:dio/dio.dart'; 11 | 12 | class ThemeListRepositoryImpl implements ThemeListRepository { 13 | @override 14 | Future> loadThemeList( 15 | String themeId, String lastId) { 16 | return _getThemeList(themeId, lastId); 17 | } 18 | } 19 | 20 | Future> _getThemeList( 21 | String themeId, String lastId) async { 22 | Dio dio =DioFactory.getInstance().getDio(); 23 | 24 | String url; 25 | 26 | 27 | if (null == lastId) { 28 | url = Constant.baseUrl + Apis.themes_list + themeId; 29 | } else { 30 | url = Constant.baseUrl + 31 | Apis.themes_list + 32 | themeId + 33 | Apis.themes_list_before + 34 | lastId; 35 | } 36 | 37 | print(url); 38 | 39 | 40 | int code; 41 | 42 | String errorMsg; 43 | 44 | ThemeListModel themeListModel; 45 | 46 | BaseModel model; 47 | 48 | try { 49 | Response response = await dio.get(url); 50 | 51 | code = response.statusCode; 52 | 53 | if (response.statusCode == HttpStatus.OK) { 54 | 55 | String description = response.data['description']; 56 | 57 | String name = response.data['name']; 58 | 59 | String image = response.data['image']; 60 | 61 | String background = response.data['background']; 62 | 63 | List stories = response.data['stories']; 64 | 65 | List editors = response.data['editors']; 66 | 67 | List editorList; 68 | 69 | List storiesList = stories.map((model) { 70 | return new ThemeListStoriesModel.fromJson(model); 71 | }).toList(); 72 | 73 | //topStories根据接口只有当天有,过去时间的topStories为空 74 | if (editors != null && editors.isNotEmpty) { 75 | editorList = editors.map((model) { 76 | return new ThemeListEditorsModel.fromJson(model); 77 | }).toList(); 78 | } 79 | 80 | themeListModel = new ThemeListModel( 81 | description: description, 82 | background: background, 83 | name: name, 84 | image: image, 85 | stories: storiesList, 86 | editors: editorList); 87 | } else { 88 | errorMsg = '服务器异常'; 89 | } 90 | } catch (exception) { 91 | errorMsg = '您的网络似乎出了什么问题'; 92 | } finally { 93 | model = new BaseModel(code: code, errorMsg: errorMsg, data: themeListModel); 94 | } 95 | 96 | return model; 97 | } 98 | -------------------------------------------------------------------------------- /lib/generated/i18n.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | // ignore_for_file: non_constant_identifier_names 7 | // ignore_for_file: camel_case_types 8 | // ignore_for_file: prefer_single_quotes 9 | 10 | //This file is automatically generated. DO NOT EDIT, all your changes would be lost. 11 | class S implements WidgetsLocalizations { 12 | const S(); 13 | 14 | static const GeneratedLocalizationsDelegate delegate = 15 | GeneratedLocalizationsDelegate(); 16 | 17 | static S of(BuildContext context) => Localizations.of(context, S); 18 | 19 | @override 20 | TextDirection get textDirection => TextDirection.ltr; 21 | } 22 | 23 | class en extends S { 24 | const en(); 25 | } 26 | 27 | class GeneratedLocalizationsDelegate extends LocalizationsDelegate { 28 | const GeneratedLocalizationsDelegate(); 29 | 30 | List get supportedLocales { 31 | return const [ 32 | Locale("en", ""), 33 | ]; 34 | } 35 | 36 | LocaleListResolutionCallback listResolution({Locale fallback}) { 37 | return (List locales, Iterable supported) { 38 | if (locales == null || locales.isEmpty) { 39 | return fallback ?? supported.first; 40 | } else { 41 | return _resolve(locales.first, fallback, supported); 42 | } 43 | }; 44 | } 45 | 46 | LocaleResolutionCallback resolution({Locale fallback}) { 47 | return (Locale locale, Iterable supported) { 48 | return _resolve(locale, fallback, supported); 49 | }; 50 | } 51 | 52 | Locale _resolve(Locale locale, Locale fallback, Iterable supported) { 53 | if (locale == null || !isSupported(locale)) { 54 | return fallback ?? supported.first; 55 | } 56 | 57 | final Locale languageLocale = Locale(locale.languageCode, ""); 58 | if (supported.contains(locale)) { 59 | return locale; 60 | } else if (supported.contains(languageLocale)) { 61 | return languageLocale; 62 | } else { 63 | final Locale fallbackLocale = fallback ?? supported.first; 64 | return fallbackLocale; 65 | } 66 | } 67 | 68 | @override 69 | Future load(Locale locale) { 70 | final String lang = getLang(locale); 71 | if (lang != null) { 72 | switch (lang) { 73 | case "en": 74 | return SynchronousFuture(const en()); 75 | default: 76 | // NO-OP. 77 | } 78 | } 79 | return SynchronousFuture(const S()); 80 | } 81 | 82 | @override 83 | bool isSupported(Locale locale) => 84 | locale != null && supportedLocales.contains(locale); 85 | 86 | @override 87 | bool shouldReload(GeneratedLocalizationsDelegate old) => false; 88 | } 89 | 90 | String getLang(Locale l) => l == null 91 | ? null 92 | : l.countryCode != null && l.countryCode.isEmpty 93 | ? l.languageCode 94 | : l.toString(); 95 | -------------------------------------------------------------------------------- /lib/zhihu/storyItem.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:zhihudaily/model/homePageModel.dart'; 4 | 5 | class StoryItem extends StatelessWidget { 6 | StoryItem({Key key, this.onTap, @required this.detail}) : super(key: key); 7 | 8 | static const double height = 120.0; 9 | final HomePageModel detail; 10 | final VoidCallback onTap; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | final ThemeData theme = Theme.of(context); 15 | theme.textTheme.headline.copyWith(color: Colors.white); 16 | final TextStyle descriptionStyle = theme.textTheme.title; 17 | 18 | return new SafeArea( 19 | top: false, 20 | bottom: false, 21 | child: new Container( 22 | padding: const EdgeInsets.all(0.0), 23 | height: height, 24 | child: new Card( 25 | child: new InkWell( 26 | onTap: onTap, 27 | child: new Row( 28 | crossAxisAlignment: CrossAxisAlignment.start, 29 | children: [ 30 | // photo and title 31 | new Expanded( 32 | child: new Padding( 33 | padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0.0), 34 | child: new DefaultTextStyle( 35 | softWrap: true, 36 | overflow: TextOverflow.ellipsis, 37 | style: descriptionStyle, 38 | child: new Column( 39 | crossAxisAlignment: CrossAxisAlignment.start, 40 | children: [ 41 | new Padding( 42 | padding: const EdgeInsets.only(bottom: 8.0), 43 | child: new Text( 44 | detail.title, 45 | maxLines: 3, 46 | style: descriptionStyle.copyWith( 47 | fontSize: 16.0, color: Colors.black87), 48 | ), 49 | ), 50 | ], 51 | ), 52 | ), 53 | ), 54 | ), 55 | new SizedBox( 56 | width: 115.0, 57 | child: new Padding( 58 | padding: const EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 8.0), 59 | child: new Stack( 60 | children: [ 61 | new Positioned.fill( 62 | child: new Container( 63 | foregroundDecoration: new BoxDecoration( 64 | image: new DecorationImage( 65 | image: new NetworkImage(detail.images[0]), 66 | fit: BoxFit.cover), 67 | ), 68 | )), 69 | ], 70 | ), 71 | ), 72 | ), 73 | ], 74 | ), 75 | )), 76 | ), 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/exampleDemo/isolateApp.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:isolate'; 4 | 5 | import 'package:flutter/material.dart'; 6 | import 'package:http/http.dart' as http; 7 | 8 | class IsolateApp extends StatelessWidget { 9 | @override 10 | Widget build(BuildContext context) { 11 | return new MaterialApp( 12 | title: 'Sample App', 13 | theme: new ThemeData( 14 | primarySwatch: Colors.blue, 15 | ), 16 | home: new SampleAppPage(), 17 | ); 18 | } 19 | } 20 | 21 | class SampleAppPage extends StatefulWidget { 22 | SampleAppPage({Key key}) : super(key: key); 23 | 24 | @override 25 | _SampleAppPageState createState() => new _SampleAppPageState(); 26 | } 27 | 28 | class _SampleAppPageState extends State { 29 | List widgets = []; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | loadData(); 35 | } 36 | 37 | showLoadingDialog() { 38 | if (widgets.length == 0) { 39 | return true; 40 | } 41 | 42 | return false; 43 | } 44 | 45 | getBody() { 46 | if (showLoadingDialog()) { 47 | return getProgressDialog(); 48 | } else { 49 | return getListView(); 50 | } 51 | } 52 | 53 | getProgressDialog() { 54 | return new Center(child: new CircularProgressIndicator()); 55 | } 56 | 57 | @override 58 | Widget build(BuildContext context) { 59 | return new Scaffold( 60 | appBar: new AppBar( 61 | title: new Text("Sample App"), 62 | ), 63 | body: getBody()); 64 | } 65 | 66 | ListView getListView() => new ListView.builder( 67 | itemCount: widgets.length, 68 | itemBuilder: (BuildContext context, int position) { 69 | return getRow(position); 70 | }); 71 | 72 | Widget getRow(int i) { 73 | return new Padding(padding: new EdgeInsets.all(10.0), child: new Text("Row ${widgets[i]["title"]}")); 74 | } 75 | 76 | loadData() async { 77 | ReceivePort receivePort = new ReceivePort(); 78 | await Isolate.spawn(dataLoader, receivePort.sendPort); 79 | 80 | // The 'echo' isolate sends it's SendPort as the first message 81 | SendPort sendPort = await receivePort.first; 82 | 83 | List msg = await sendReceive(sendPort, "https://jsonplaceholder.typicode.com/posts"); 84 | 85 | setState(() { 86 | widgets = msg; 87 | }); 88 | } 89 | 90 | // the entry point for the isolate 91 | static dataLoader(SendPort sendPort) async { 92 | // Open the ReceivePort for incoming messages. 93 | ReceivePort port = new ReceivePort(); 94 | 95 | // Notify any other isolates what port this isolate listens to. 96 | sendPort.send(port.sendPort); 97 | 98 | await for (var msg in port) { 99 | String data = msg[0]; 100 | SendPort replyTo = msg[1]; 101 | 102 | String dataURL = data; 103 | http.Response response = await http.get(dataURL); 104 | // Lots of JSON to parse 105 | // replyTo.send(JSON.decode(response.body)); 106 | } 107 | } 108 | 109 | Future sendReceive(SendPort port, msg) { 110 | ReceivePort response = new ReceivePort(); 111 | port.send([msg, response.sendPort]); 112 | return response.first; 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /lib/exampleDemo/platformChannel.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | 6 | class ChannelApp extends StatelessWidget { 7 | // This widget is the root of your application. 8 | @override 9 | Widget build(BuildContext context) { 10 | return new MaterialApp( 11 | title: 'PlatformChannel App', 12 | theme: new ThemeData( 13 | primarySwatch: Colors.blue, 14 | ), 15 | home: new PlatformChannel(), 16 | ); 17 | } 18 | } 19 | 20 | class PlatformChannel extends StatefulWidget { 21 | @override 22 | _PlatformChannelState createState() => new _PlatformChannelState(); 23 | } 24 | 25 | class _PlatformChannelState extends State { 26 | static const MethodChannel methodChannel = 27 | const MethodChannel('samples.flutter.io/battery'); 28 | static const MethodChannel gotoChannel = 29 | const MethodChannel('samples.flutter.io/intent'); 30 | static const EventChannel eventChannel = 31 | const EventChannel('samples.flutter.io/charging'); 32 | 33 | String _batteryLevel = 'Battery level: unknown.'; 34 | String _chargingStatus = 'Battery status: unknown.'; 35 | 36 | Future _getBatteryLevel() async { 37 | String batteryLevel; 38 | try { 39 | final int result = await methodChannel.invokeMethod('getBatteryLevel'); 40 | batteryLevel = 'Battery level: $result%.'; 41 | } on PlatformException { 42 | batteryLevel = 'Failed to get battery level.'; 43 | } 44 | setState(() { 45 | _batteryLevel = batteryLevel; 46 | }); 47 | } 48 | 49 | Future _gotoActivity() async { 50 | gotoChannel.invokeMethod('gotoActivity'); 51 | } 52 | 53 | @override 54 | void initState() { 55 | super.initState(); 56 | eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError); 57 | _getBatteryLevel(); 58 | } 59 | 60 | void _onEvent(Object event) { 61 | setState(() { 62 | _chargingStatus = 63 | "Battery status: ${event == 'charging' ? '' : 'dis'}charging."; 64 | }); 65 | } 66 | 67 | void _onError(Object error) { 68 | setState(() { 69 | _chargingStatus = 'Battery status: unknown.'; 70 | }); 71 | } 72 | 73 | @override 74 | Widget build(BuildContext context) { 75 | return new Scaffold( 76 | appBar: new AppBar( 77 | title: new Text('PlatformChannel App'), 78 | ), 79 | body: new Center( 80 | child: new Column( 81 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 82 | children: [ 83 | new Column( 84 | mainAxisAlignment: MainAxisAlignment.center, 85 | children: [ 86 | new Text(_batteryLevel, key: const Key('Battery level label')), 87 | new Padding( 88 | padding: const EdgeInsets.all(16.0), 89 | child: new RaisedButton( 90 | child: const Text('Refresh'), 91 | onPressed: _getBatteryLevel, 92 | ), 93 | ), 94 | ], 95 | ), 96 | new Text(_chargingStatus), 97 | ], 98 | ), 99 | ), 100 | floatingActionButton: new FloatingActionButton( 101 | tooltip: 'Fade', 102 | child: new Icon(Icons.arrow_forward_ios), 103 | onPressed: () { 104 | _gotoActivity(); 105 | }, 106 | ), 107 | ); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /lib/zhihu/zhihudaily.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:http/http.dart' as http; 6 | import 'package:zhihudaily/model/homePageModel.dart'; 7 | import 'package:zhihudaily/utils/RouterUtils.dart'; 8 | import 'package:zhihudaily/widget/drawerContent.dart'; 9 | import 'package:zhihudaily/widget/homeBanner.dart'; 10 | import 'package:zhihudaily/zhihu/storyItem.dart'; 11 | 12 | class ZhihuDailyApp extends StatelessWidget { 13 | @override 14 | Widget build(BuildContext context) { 15 | return new MaterialApp( 16 | title: '知乎日报', 17 | color: Colors.grey, 18 | theme: new ThemeData( 19 | primarySwatch: Colors.blue, 20 | ), 21 | home: new SampleAppPage(), 22 | ); 23 | } 24 | } 25 | 26 | class SampleAppPage extends StatefulWidget { 27 | SampleAppPage({Key key}) : super(key: key); 28 | 29 | @override 30 | _SampleAppPageState createState() => new _SampleAppPageState(); 31 | } 32 | 33 | class _SampleAppPageState extends State { 34 | List homePageDataList = new List(); 35 | List topBannerModel; 36 | 37 | @override 38 | void initState() { 39 | super.initState(); 40 | loadData(); 41 | } 42 | 43 | Widget buildItem(BuildContext context, int position) { 44 | Widget widget; 45 | 46 | HomePageModel item = homePageDataList[position]; 47 | 48 | switch (item.itemType) { 49 | case HomePageModel.itemTypeBanner: 50 | widget = new HomeBanner(topBannerModel); 51 | break; 52 | case HomePageModel.itemTypeNormal: 53 | widget = getItem(context, position); 54 | break; 55 | } 56 | 57 | return widget; 58 | } 59 | 60 | @override 61 | Widget build(BuildContext context) { 62 | return new Scaffold( 63 | appBar: new AppBar( 64 | title: new Text("知乎日报"), 65 | ), 66 | body: new ListView.builder( 67 | itemCount: homePageDataList.length, 68 | itemBuilder: (BuildContext context, int position) { 69 | return buildItem(context, position); 70 | }), 71 | drawer: new Drawer( 72 | child: new DrawerPage(), 73 | ) 74 | ); 75 | } 76 | 77 | Widget getItem(BuildContext context, int i) { 78 | return new Container( 79 | margin: const EdgeInsets.only(left: 4.0, right: 4.0), 80 | child: new StoryItem( 81 | detail: homePageDataList[i], 82 | onTap: () { 83 | RouterUtils.startWebView(context, homePageDataList[i].id); 84 | }, 85 | )); 86 | } 87 | 88 | 89 | loadData() async { 90 | String dataURL = "https://news-at.zhihu.com/api/4/news/latest"; 91 | http.Response response = await http.get(dataURL); 92 | 93 | List banner = json.decode(response.body)["top_stories"]; 94 | List storise = json.decode(response.body)["stories"]; 95 | 96 | if (storise.isNotEmpty) { 97 | homePageDataList = storise.map((model) { 98 | return new HomePageModel.fromJson(model); 99 | }).toList(); 100 | } 101 | 102 | if (banner.isNotEmpty) { 103 | topBannerModel = banner.map((model) { 104 | return new TopStoriesModel.fromJson(model); 105 | }).toList(); 106 | 107 | HomePageModel top = new HomePageModel(); 108 | top.setItemType(HomePageModel.itemTypeBanner); 109 | homePageDataList.insert(0, top); 110 | } 111 | 112 | setState(() {}); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /lib/widget/homeBanner.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:zhihudaily/model/homePageModel.dart'; 5 | import 'package:zhihudaily/utils/RouterUtils.dart'; 6 | 7 | class HomeBanner extends StatefulWidget { 8 | final List bannerList; 9 | 10 | HomeBanner(this.bannerList); 11 | 12 | @override 13 | State createState() => new HomeBannerState(); 14 | } 15 | 16 | class HomeBannerState extends State { 17 | List _indicators = []; 18 | 19 | int _curIndicatorsIndex = 0; 20 | 21 | Timer timer; 22 | 23 | PageController pageController = new PageController(initialPage: 0); 24 | 25 | @override 26 | void initState() { 27 | super.initState(); 28 | timer = new Timer.periodic(new Duration(seconds: 3), (timer) { 29 | pageController.animateToPage(_curIndicatorsIndex == _indicators.length - 1 ? 0 : _curIndicatorsIndex + 1, 30 | duration: new Duration(milliseconds: 500), curve: Curves.linear); 31 | }); 32 | } 33 | 34 | @override 35 | void dispose() { 36 | super.dispose(); 37 | timer.cancel(); 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return buildBanner(); 43 | } 44 | 45 | Widget buildBanner() { 46 | return new Container( 47 | height: 220.0, 48 | child: new Stack( 49 | children: [ 50 | buildPagerView(), 51 | buildIndicators(), 52 | ], 53 | ), 54 | ); 55 | } 56 | 57 | Widget buildPagerView() { 58 | return new PageView.builder( 59 | controller: pageController, 60 | itemBuilder: (BuildContext context, int index) { 61 | return buildItem(context, index); 62 | }, 63 | itemCount: widget.bannerList.length, 64 | onPageChanged: (index) { 65 | _changePage(index); 66 | }, 67 | ); 68 | } 69 | 70 | Widget buildItem(BuildContext context, int index) { 71 | TopStoriesModel banner = widget.bannerList[index]; 72 | 73 | return new GestureDetector( 74 | onTap: () { 75 | RouterUtils.startWebView(context, banner.id); 76 | }, 77 | child: new Image.network( 78 | banner.image, 79 | fit: BoxFit.fitWidth, 80 | height: 200.0, 81 | ), 82 | ); 83 | } 84 | 85 | Widget buildIndicators() { 86 | _initIndicators(); 87 | return new Align( 88 | alignment: Alignment.bottomCenter, 89 | child: new Container( 90 | color: Colors.black38, 91 | height: 60.0, 92 | width: double.infinity, 93 | child: new Column( 94 | children: [ 95 | new Padding( 96 | padding: const EdgeInsets.fromLTRB(0.0, 8.0, 0.0, 0.0), 97 | child: new Text(widget.bannerList[_curIndicatorsIndex].title, 98 | maxLines: 1, 99 | style: new TextStyle(color: Colors.white, fontSize: 16.0)), 100 | ), 101 | new Padding( 102 | padding: const EdgeInsets.fromLTRB(0.0, 16.0, 0.0, 0.0), 103 | child: new SizedBox( 104 | width: widget.bannerList.length * 12.0, 105 | height: 6.0, 106 | child: new Row( 107 | children: _indicators, 108 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 109 | ), 110 | ), 111 | ) 112 | ], 113 | ), 114 | ), 115 | ); 116 | } 117 | 118 | _initIndicators() { 119 | _indicators.clear(); 120 | for (int i = 0; i < widget.bannerList.length; i++) { 121 | _indicators.add(new CircleAvatar( 122 | radius: 6.0, 123 | backgroundColor: i == _curIndicatorsIndex ? Colors.white : Colors.grey, 124 | )); 125 | } 126 | } 127 | 128 | _changePage(int index) { 129 | _curIndicatorsIndex = index; 130 | setState(() {}); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /lib/widget/drawerContent.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:zhihudaily/model/ThemeModel.dart'; 3 | import 'package:http/http.dart' as http; 4 | import 'dart:convert'; 5 | import 'package:zhihudaily/common/common_loading_dialog.dart'; 6 | import 'package:zhihudaily/utils/RouterUtils.dart'; 7 | 8 | class DrawerBody extends StatefulWidget { 9 | @override 10 | DrawerState createState() => new DrawerState(); 11 | } 12 | 13 | class DrawerState extends State { 14 | List themes = []; 15 | 16 | @override 17 | void initState() { 18 | super.initState(); 19 | loadData(); 20 | } 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return new Scaffold( 25 | backgroundColor: Colors.white, 26 | body: buildBody(), 27 | ); 28 | } 29 | 30 | Widget buildBody() { 31 | if (null != themes && themes.isNotEmpty) { 32 | return new Column( 33 | children: [ 34 | buildDrawer(), 35 | buildItem(), 36 | new Divider(height: 1.0), 37 | buildList() 38 | ], 39 | ); 40 | } else { 41 | return ProgressDialog.buildProgressDialog(); 42 | } 43 | } 44 | 45 | Widget buildDrawer() { 46 | return new UserAccountsDrawerHeader( 47 | onDetailsPressed: () {}, 48 | accountName: new Text('MelonRice'), 49 | accountEmail: new Text('rice@bmqb.com'), 50 | currentAccountPicture: new CircleAvatar( 51 | backgroundImage: new NetworkImage( 52 | 'https://wx3.sinaimg.cn/mw690/44485fa4gy1ft4q8gk1vmj205k05kjsn.jpg'), 53 | ), 54 | ); 55 | } 56 | 57 | Widget buildItem() { 58 | return new InkWell( 59 | onTap: () { 60 | Navigator.of(context).pop(); 61 | RouterUtils.routeToMain(context); 62 | }, 63 | child: new Padding( 64 | padding: const EdgeInsets.only(left: 10.0), 65 | child: new Row( 66 | children: [ 67 | new Icon(Icons.home, color: Colors.blue, size: 36.0), 68 | new Padding( 69 | padding: const EdgeInsets.only(left: 10.0, right: 10.0), 70 | child: new Text('首页', 71 | style: 72 | new TextStyle(color: Colors.blue, fontSize: 18.0))), 73 | ], 74 | )), 75 | ); 76 | } 77 | 78 | Widget buildList() { 79 | return new MediaQuery.removePadding( 80 | context: context, 81 | // DrawerHeader consumes top MediaQuery padding. 82 | removeTop: true, 83 | child: new Expanded( 84 | child: new ListView( 85 | children: [ 86 | new Column( 87 | mainAxisSize: MainAxisSize.min, 88 | crossAxisAlignment: CrossAxisAlignment.stretch, 89 | children: themes.map((ThemeModel model) { 90 | return buildOtherItem(model); 91 | }).toList(), 92 | ), 93 | ], 94 | )), 95 | ); 96 | } 97 | 98 | Widget buildOtherItem(ThemeModel model) { 99 | return new InkWell( 100 | onTap: () { 101 | Navigator.of(context).pop(); 102 | RouterUtils.route2ThemeList(context, '${model.id}'); 103 | }, 104 | child: new ListTile( 105 | trailing: new Icon(Icons.add, color: Colors.grey[300]), 106 | title: new Text('${model.name}', 107 | style: new TextStyle( 108 | fontWeight: FontWeight.bold, 109 | color: Colors.grey[700], 110 | fontSize: 16.0)), 111 | ), 112 | ); 113 | } 114 | 115 | loadData() async { 116 | String dataURL = "https://news-at.zhihu.com/api/4/themes"; 117 | http.Response response = await http.get(dataURL); 118 | List others = json.decode(response.body)["others"]; 119 | themes = others.map((model) { 120 | return new ThemeModel.fromJson(model); 121 | }).toList(); 122 | 123 | setState(() {}); 124 | } 125 | } 126 | 127 | class DrawerPage extends StatelessWidget { 128 | @override 129 | Widget build(BuildContext context) { 130 | return new Scaffold( 131 | backgroundColor: Colors.white, 132 | body: new DrawerBody(), 133 | ); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /lib/exampleDemo/startApp.dart: -------------------------------------------------------------------------------- 1 | // Step 7 (Final): Change the app's theme 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:english_words/english_words.dart'; 5 | 6 | class StartApp extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | //MaterialApp widget本身是一个 widget 的 StatelessWidget 类 10 | //在 Flutter 中,大多数时候一切都可以看作 widget , 包括 alignment,padding 和 layout 11 | return new MaterialApp( 12 | //这个不是标题栏title 13 | title: 'Startup Name Generator', 14 | //主题颜色 15 | theme: new ThemeData( 16 | primaryColor: Colors.black, 17 | ), 18 | //StatelessWidget嵌套了一个StatefulWidget 19 | home: new RandomWords(), 20 | ); 21 | } 22 | } 23 | 24 | class RandomWords extends StatefulWidget { 25 | //只需要实现一个方法,返回一个State 26 | @override 27 | createState() => new RandomWordsState(); 28 | } 29 | 30 | class RandomWordsState extends State { 31 | //保存建议的单词对 32 | //final关键字让变量不能被再次赋值 33 | //在Dart语言中使用下划线前缀标识符,会强制其变成私有的 34 | final _suggestions = []; 35 | 36 | //存储用户收藏的单词对 37 | final _saved = new Set(); 38 | 39 | //自定义字体大小 40 | final _biggerFont = const TextStyle(fontSize: 18.0); 41 | 42 | @override 43 | void initState() { 44 | //重写initState,以完成仅需要执行一次的工作 45 | } 46 | 47 | //将 Widget build(BuildContext context)方法放在State上而不是StatefulWidget上是为了在继承StatefulWidget时能更加灵活 48 | @override 49 | Widget build(BuildContext context) { 50 | return new Scaffold( 51 | appBar: new AppBar( 52 | title: new Text('Startup Name Generator111111111'), 53 | //右上角事件,进行页面跳转 54 | actions: [ 55 | new IconButton(icon: new Icon(Icons.list), onPressed: _pushSaved) 56 | ], 57 | ), 58 | body: _buildSuggestions(), 59 | ); 60 | } 61 | 62 | //构建显示建议单词对的ListView,当不知道要返回的组件属于什么类型,都可以定义为Widget 63 | Widget _buildSuggestions() { 64 | return new ListView.builder( 65 | padding: const EdgeInsets.all(16.0), 66 | // 对于每个建议的单词对都会调用一次itemBuilder,然后将单词对添加到ListTile行中 67 | //不指定itemCount时,是一个无限长度的列表 68 | itemBuilder: (context, i) { 69 | // 在奇数行,该行添加一个分割线widget,来分隔相邻的词对。 70 | if (i.isOdd) return new Divider(); 71 | // 在偶数行,该函数会为单词对添加一个ListTile row. 72 | // 语法 "i ~/ 2" 表示i除以2,但返回值是整形(向下取整),比如i为:1, 2, 3, 4, 5 73 | // 时,结果为0, 1, 1, 2, 2, 这可以计算出ListView中减去分隔线后的实际单词对数量 74 | final index = i ~/ 2; 75 | // 如果是建议列表中最后一个单词对 76 | if (index >= _suggestions.length) { 77 | // ...接着再生成10个单词对,然后添加到建议列表 78 | _suggestions.addAll(generateWordPairs().take(10)); 79 | } 80 | return _buildRow(_suggestions[index]); 81 | }, 82 | ); 83 | } 84 | 85 | Widget _buildRow(WordPair pair) { 86 | final alreadySaved = _saved.contains(pair); 87 | return new ListTile( 88 | title: new Text( 89 | pair.asPascalCase, 90 | style: _biggerFont, 91 | ), 92 | trailing: new Icon( 93 | alreadySaved ? Icons.album : Icons.favorite_border, 94 | color: alreadySaved ? Colors.red : null, 95 | ), 96 | //在 _buildRow中让心形❤图标变得可以点击。如果单词条目已经添加到收藏夹中, 97 | // 再次点击它将其从收藏夹中删除。当心形❤图标被点击时,函数调用setState()通知框架状态已经改变。 98 | //只要添加了onTap属性,在MD主题下会有水波纹点击效果 99 | onTap: () { 100 | setState( 101 | () { 102 | if (alreadySaved) { 103 | _saved.remove(pair); 104 | } else { 105 | _saved.add(pair); 106 | } 107 | }, 108 | ); 109 | }, 110 | ); 111 | } 112 | 113 | void _pushSaved() { 114 | //当用户点击导航栏中的列表图标时,建立一个路由并将其推入到导航管理器栈中。此操作会切换页面以显示新路由。 115 | Navigator.of(context).push( 116 | new MaterialPageRoute( 117 | builder: (context) { 118 | final tiles = _saved.map( 119 | (pair) { 120 | return new ListTile( 121 | title: new Text( 122 | pair.asPascalCase, 123 | style: _biggerFont, 124 | ), 125 | ); 126 | }, 127 | ); 128 | final divided = ListTile 129 | .divideTiles( 130 | context: context, 131 | tiles: tiles, 132 | ) 133 | .toList(); 134 | 135 | return new Scaffold( 136 | appBar: new AppBar( 137 | title: new Text('Saved Suggestions'), 138 | ), 139 | body: new ListView(children: divided), 140 | ); 141 | }, 142 | ), 143 | ); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/yourcompany/starter/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.yourcompany.starter; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.ContextWrapper; 6 | import android.content.Intent; 7 | import android.content.IntentFilter; 8 | import android.os.BatteryManager; 9 | import android.os.Build; 10 | import android.os.Bundle; 11 | 12 | import junit.framework.Test; 13 | 14 | import io.flutter.app.FlutterActivity; 15 | import io.flutter.plugin.common.EventChannel; 16 | import io.flutter.plugin.common.EventChannel.EventSink; 17 | import io.flutter.plugin.common.EventChannel.StreamHandler; 18 | import io.flutter.plugin.common.MethodCall; 19 | import io.flutter.plugin.common.MethodChannel; 20 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler; 21 | import io.flutter.plugin.common.MethodChannel.Result; 22 | import io.flutter.plugins.GeneratedPluginRegistrant; 23 | 24 | public class MainActivity extends FlutterActivity { 25 | private static final String BATTERY_CHANNEL = "samples.flutter.io/battery"; 26 | private static final String CHARGING_CHANNEL = "samples.flutter.io/charging"; 27 | private static final String INTENT_CHANNEL = "samples.flutter.io/intent"; 28 | 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | GeneratedPluginRegistrant.registerWith(this); 33 | new EventChannel(getFlutterView(), CHARGING_CHANNEL).setStreamHandler(new StreamHandler() { 34 | private BroadcastReceiver chargingStateChangeReceiver; 35 | 36 | @Override 37 | public void onListen(Object arguments, EventSink events) { 38 | chargingStateChangeReceiver = createChargingStateChangeReceiver(events); 39 | registerReceiver(chargingStateChangeReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 40 | } 41 | 42 | @Override 43 | public void onCancel(Object arguments) { 44 | unregisterReceiver(chargingStateChangeReceiver); 45 | chargingStateChangeReceiver = null; 46 | } 47 | }); 48 | 49 | new MethodChannel(getFlutterView(), INTENT_CHANNEL).setMethodCallHandler(new MethodCallHandler() { 50 | @Override 51 | public void onMethodCall(MethodCall call, Result result) { 52 | if (call.method.equals("gotoActivity")) { 53 | Intent intent = new Intent(MainActivity.this, TestActivity.class); 54 | startActivity(intent); 55 | } else { 56 | result.notImplemented(); 57 | } 58 | } 59 | }); 60 | 61 | new MethodChannel(getFlutterView(), BATTERY_CHANNEL).setMethodCallHandler(new MethodCallHandler() { 62 | @Override 63 | public void onMethodCall(MethodCall call, Result result) { 64 | if (call.method.equals("getBatteryLevel")) { 65 | int batteryLevel = getBatteryLevel(); 66 | 67 | if (batteryLevel != -1) { 68 | result.success(batteryLevel); 69 | } else { 70 | result.error("UNAVAILABLE", "Battery level not available.", null); 71 | } 72 | } else { 73 | result.notImplemented(); 74 | } 75 | } 76 | }); 77 | } 78 | 79 | private BroadcastReceiver createChargingStateChangeReceiver(final EventSink events) { 80 | return new BroadcastReceiver() { 81 | @Override 82 | public void onReceive(Context context, Intent intent) { 83 | int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1); 84 | 85 | if (status == BatteryManager.BATTERY_STATUS_UNKNOWN) { 86 | events.error("UNAVAILABLE", "Charging status unavailable", null); 87 | } else { 88 | boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING 89 | || status == BatteryManager.BATTERY_STATUS_FULL; 90 | events.success(isCharging ? "charging" : "discharging"); 91 | } 92 | } 93 | }; 94 | } 95 | 96 | private int getBatteryLevel() { 97 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 98 | BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE); 99 | return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY); 100 | } else { 101 | Intent intent = new ContextWrapper(getApplicationContext()). 102 | registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 103 | return (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) / intent.getIntExtra( 104 | BatteryManager.EXTRA_SCALE, -1); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://www.dartlang.org/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.0.8" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "1.0.4" 18 | charcode: 19 | dependency: transitive 20 | description: 21 | name: charcode 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.2" 25 | collection: 26 | dependency: transitive 27 | description: 28 | name: collection 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.14.11" 32 | cookie_jar: 33 | dependency: transitive 34 | description: 35 | name: cookie_jar 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "0.0.7" 39 | cupertino_icons: 40 | dependency: "direct main" 41 | description: 42 | name: cupertino_icons 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "0.1.2" 46 | dio: 47 | dependency: "direct main" 48 | description: 49 | name: dio 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.0.9" 53 | english_words: 54 | dependency: "direct main" 55 | description: 56 | name: english_words 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "3.1.5" 60 | flutter: 61 | dependency: "direct main" 62 | description: flutter 63 | source: sdk 64 | version: "0.0.0" 65 | flutter_test: 66 | dependency: "direct dev" 67 | description: flutter 68 | source: sdk 69 | version: "0.0.0" 70 | flutter_webview_plugin: 71 | dependency: "direct main" 72 | description: 73 | name: flutter_webview_plugin 74 | url: "https://pub.dartlang.org" 75 | source: hosted 76 | version: "0.3.0+2" 77 | http: 78 | dependency: "direct main" 79 | description: 80 | name: http 81 | url: "https://pub.dartlang.org" 82 | source: hosted 83 | version: "0.12.0" 84 | http_parser: 85 | dependency: transitive 86 | description: 87 | name: http_parser 88 | url: "https://pub.dartlang.org" 89 | source: hosted 90 | version: "3.1.3" 91 | matcher: 92 | dependency: transitive 93 | description: 94 | name: matcher 95 | url: "https://pub.dartlang.org" 96 | source: hosted 97 | version: "0.12.3+1" 98 | meta: 99 | dependency: transitive 100 | description: 101 | name: meta 102 | url: "https://pub.dartlang.org" 103 | source: hosted 104 | version: "1.1.6" 105 | path: 106 | dependency: transitive 107 | description: 108 | name: path 109 | url: "https://pub.dartlang.org" 110 | source: hosted 111 | version: "1.6.2" 112 | quiver: 113 | dependency: transitive 114 | description: 115 | name: quiver 116 | url: "https://pub.dartlang.org" 117 | source: hosted 118 | version: "2.0.1" 119 | sky_engine: 120 | dependency: transitive 121 | description: flutter 122 | source: sdk 123 | version: "0.0.99" 124 | source_span: 125 | dependency: transitive 126 | description: 127 | name: source_span 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "1.4.1" 131 | stack_trace: 132 | dependency: transitive 133 | description: 134 | name: stack_trace 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "1.9.3" 138 | stream_channel: 139 | dependency: transitive 140 | description: 141 | name: stream_channel 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "1.6.8" 145 | string_scanner: 146 | dependency: transitive 147 | description: 148 | name: string_scanner 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "1.0.4" 152 | term_glyph: 153 | dependency: transitive 154 | description: 155 | name: term_glyph 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "1.0.1" 159 | test_api: 160 | dependency: transitive 161 | description: 162 | name: test_api 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "0.2.1" 166 | transparent_image: 167 | dependency: "direct main" 168 | description: 169 | name: transparent_image 170 | url: "https://pub.dartlang.org" 171 | source: hosted 172 | version: "0.1.0" 173 | typed_data: 174 | dependency: transitive 175 | description: 176 | name: typed_data 177 | url: "https://pub.dartlang.org" 178 | source: hosted 179 | version: "1.1.6" 180 | vector_math: 181 | dependency: transitive 182 | description: 183 | name: vector_math 184 | url: "https://pub.dartlang.org" 185 | source: hosted 186 | version: "2.0.8" 187 | sdks: 188 | dart: ">=2.0.0 <3.0.0" 189 | -------------------------------------------------------------------------------- /lib/exampleDemo/layoutApp.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class LayoutApp extends StatelessWidget { 4 | @override 5 | Widget build(BuildContext context) { 6 | return MaterialApp( 7 | title: 'Flutter Demo', 8 | theme: ThemeData( 9 | primarySwatch: Colors.green, 10 | ), 11 | home: MyHomePage(title: 'Strawberry Pavlova Recipe'), 12 | ); 13 | } 14 | } 15 | 16 | class MyHomePage extends StatefulWidget { 17 | MyHomePage({Key key, this.title}) : super(key: key); 18 | 19 | final String title; 20 | 21 | @override 22 | _MyHomePageState createState() => _MyHomePageState(); 23 | } 24 | 25 | class _MyHomePageState extends State { 26 | @override 27 | Widget build(BuildContext context) { 28 | var titleText = Container( 29 | padding: EdgeInsets.all(0.0), 30 | child: Text( 31 | 'Strawberry Pavlova', 32 | style: TextStyle( 33 | fontWeight: FontWeight.w800, 34 | letterSpacing: 0.5, 35 | fontSize: 30.0, 36 | ), 37 | ), 38 | ); 39 | 40 | var subTitle = Text( 41 | 'Pavlova is a meringue-based dessert named after the Russian ballerina Anna Pavlova. Pavlova features a crisp crust and soft, light inside, topped with fruit and whipped cream.', 42 | textAlign: TextAlign.center, 43 | style: TextStyle( 44 | fontFamily: 'Georgia', 45 | fontSize: 25.0, 46 | ), 47 | ); 48 | 49 | var ratings = Container( 50 | padding: EdgeInsets.all(20.0), 51 | child: Row( 52 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 53 | children: [ 54 | Row( 55 | mainAxisSize: MainAxisSize.min, 56 | children: [ 57 | Icon(Icons.star, color: Colors.black), 58 | Icon(Icons.star, color: Colors.black), 59 | Icon(Icons.star, color: Colors.black), 60 | Icon(Icons.star, color: Colors.black), 61 | Icon(Icons.star, color: Colors.black), 62 | ], 63 | ), 64 | Text( 65 | '170 Reviews', 66 | style: TextStyle( 67 | color: Colors.black, 68 | fontWeight: FontWeight.w800, 69 | fontFamily: 'Roboto', 70 | letterSpacing: 0.5, 71 | fontSize: 20.0, 72 | ), 73 | ), 74 | ], 75 | ), 76 | ); 77 | 78 | var descTextStyle = TextStyle( 79 | color: Colors.black, 80 | fontWeight: FontWeight.w800, 81 | fontFamily: 'Roboto', 82 | letterSpacing: 0.5, 83 | fontSize: 18.0, 84 | height: 2.0, 85 | ); 86 | 87 | // DefaultTextStyle.merge allows you to create a default text 88 | // style that is inherited by its child and all subsequent children. 89 | var iconList = DefaultTextStyle.merge( 90 | style: descTextStyle, 91 | child: Container( 92 | padding: EdgeInsets.all(20.0), 93 | child: Row( 94 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 95 | children: [ 96 | Column( 97 | children: [ 98 | Icon(Icons.kitchen, color: Colors.green[500]), 99 | Text('PREP:'), 100 | Text('25 min'), 101 | ], 102 | ), 103 | Column( 104 | children: [ 105 | Icon(Icons.timer, color: Colors.green[500]), 106 | Text('COOK:'), 107 | Text('1 hr'), 108 | ], 109 | ), 110 | Column( 111 | children: [ 112 | Icon(Icons.restaurant, color: Colors.green[500]), 113 | Text('FEEDS:'), 114 | Text('4-6'), 115 | ], 116 | ), 117 | ], 118 | ), 119 | ), 120 | ); 121 | 122 | var leftColumn = Container( 123 | padding: EdgeInsets.fromLTRB(20.0, 30.0, 20.0, 20.0), 124 | child: Column( 125 | mainAxisAlignment: MainAxisAlignment.spaceAround, 126 | children: [ 127 | titleText, 128 | subTitle, 129 | ratings, 130 | iconList, 131 | ], 132 | ), 133 | ); 134 | 135 | var mainImage = Image.asset( 136 | 'lib/images/pavlova.jpg', 137 | fit: BoxFit.fill, 138 | ); 139 | 140 | return Scaffold( 141 | appBar: AppBar( 142 | title: Text(widget.title), 143 | ), 144 | body: Center( 145 | child: Container( 146 | margin: EdgeInsets.fromLTRB(0.0, 40.0, 0.0, 30.0), 147 | height: 600.0, 148 | child: Card( 149 | child: Row( 150 | crossAxisAlignment: CrossAxisAlignment.stretch, 151 | children: [ 152 | Expanded( 153 | flex: 1, 154 | child: Container( 155 | width: 440.0, 156 | child: leftColumn, 157 | )), 158 | Expanded( 159 | flex: 1, 160 | child: mainImage, 161 | ), 162 | ], 163 | ), 164 | ), 165 | ), 166 | ), 167 | ); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /flutter_01.log: -------------------------------------------------------------------------------- 1 | Flutter crash report; please file at https://github.com/flutter/flutter/issues. 2 | 3 | ## command 4 | 5 | flutter upgrade 6 | 7 | ## exception 8 | 9 | ArgumentError: Invalid argument(s): Cannot find executable for /Users/lhw/flutter/bin/cache/dart-sdk/bin/pub. 10 | 11 | ``` 12 | #0 _getExecutable (package:process/src/interface/local_process_manager.dart:113) 13 | #1 LocalProcessManager.start (package:process/src/interface/local_process_manager.dart:41) 14 | #2 runCommand (package:flutter_tools/src/base/process.dart:115) 15 | #3 runCommandAndStreamOutput (package:flutter_tools/src/base/process.dart:133) 16 | 17 | #4 pub (package:flutter_tools/src/dart/pub.dart:153) 18 | 19 | #5 pubGet (package:flutter_tools/src/dart/pub.dart:104) 20 | 21 | #6 UpgradeCommand.runCommand (package:flutter_tools/src/commands/upgrade.dart:75) 22 | 23 | #7 FlutterCommand.verifyThenRunCommand (package:flutter_tools/src/runner/flutter_command.dart:344) 24 | 25 | #8 FlutterCommand.run. (package:flutter_tools/src/runner/flutter_command.dart:279) 26 | 27 | #9 AppContext.run. (package:flutter_tools/src/base/context.dart:142) 28 | 29 | #10 _rootRun (dart:async/zone.dart:1126) 30 | #11 _CustomZone.run (dart:async/zone.dart:1023) 31 | #12 runZoned (dart:async/zone.dart:1501) 32 | #13 AppContext.run (package:flutter_tools/src/base/context.dart:141) 33 | 34 | #14 FlutterCommand.run (package:flutter_tools/src/runner/flutter_command.dart:270) 35 | #15 CommandRunner.runCommand (package:args/command_runner.dart:194) 36 | 37 | #16 FlutterCommandRunner.runCommand. (package:flutter_tools/src/runner/flutter_command_runner.dart:309) 38 | 39 | #17 AppContext.run. (package:flutter_tools/src/base/context.dart:142) 40 | 41 | #18 _rootRun (dart:async/zone.dart:1126) 42 | #19 _CustomZone.run (dart:async/zone.dart:1023) 43 | #20 runZoned (dart:async/zone.dart:1501) 44 | #21 AppContext.run (package:flutter_tools/src/base/context.dart:141) 45 | 46 | #22 FlutterCommandRunner.runCommand (package:flutter_tools/src/runner/flutter_command_runner.dart:265) 47 | 48 | #23 CommandRunner.run. (package:args/command_runner.dart:109) 49 | #24 new Future.sync (dart:async/future.dart:222) 50 | #25 CommandRunner.run (package:args/command_runner.dart:109) 51 | #26 FlutterCommandRunner.run (package:flutter_tools/src/runner/flutter_command_runner.dart:174) 52 | #27 run. (package:flutter_tools/runner.dart:59) 53 | 54 | #28 AppContext.run. (package:flutter_tools/src/base/context.dart:142) 55 | 56 | #29 _rootRun (dart:async/zone.dart:1126) 57 | #30 _CustomZone.run (dart:async/zone.dart:1023) 58 | #31 runZoned (dart:async/zone.dart:1501) 59 | #32 AppContext.run (package:flutter_tools/src/base/context.dart:141) 60 | 61 | #33 runInContext (package:flutter_tools/src/context_runner.dart:43) 62 | 63 | #34 run (package:flutter_tools/runner.dart:50) 64 | #35 main (package:flutter_tools/executable.dart:49) 65 | 66 | #36 main (file:///Users/lhw/flutter/packages/flutter_tools/bin/flutter_tools.dart:8) 67 | #37 _startIsolate. (dart:isolate-patch/dart:isolate/isolate_patch.dart:277) 68 | #38 _RawReceivePortImpl._handleMessage (dart:isolate-patch/dart:isolate/isolate_patch.dart:165) 69 | ``` 70 | 71 | ## flutter doctor 72 | 73 | ``` 74 | [✓] Flutter (Channel beta, v0.5.1, on Mac OS X 10.14.1 18B75, locale zh-Hans-CN) 75 | • Flutter version 0.5.1 at /Users/lhw/flutter 76 | • Framework revision c7ea3ca377 (7 months ago), 2018-11-29 19:41:26 -0800 77 | • Engine revision 7375a0f414 78 | • Dart version 2.0.0-dev.58.0.flutter-f981f09760 79 | 80 | [!] Android toolchain - develop for Android devices (Android SDK 28.0.3) 81 | • Android SDK at /Users/lhw/android-sdks 82 | • Android NDK at /Users/lhw/android-sdks/ndk-bundle 83 | • Platform android-28, build-tools 28.0.3 84 | • ANDROID_HOME = /Users/lhw/android-sdks 85 | • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java 86 | • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06) 87 | ! Some Android licenses not accepted. To resolve this, run: flutter doctor --android-licenses 88 | 89 | [✓] iOS toolchain - develop for iOS devices (Xcode 10.1) 90 | • Xcode at /Applications/Xcode.app/Contents/Developer 91 | • Xcode 10.1, Build version 10B61 92 | • ios-deploy 1.9.2 93 | • CocoaPods version 1.5.3 94 | 95 | [✓] Android Studio (version 3.2) 96 | • Android Studio at /Applications/Android Studio.app/Contents 97 | ✗ Flutter plugin not installed; this adds Flutter specific functionality. 98 | ✗ Dart plugin not installed; this adds Dart specific functionality. 99 | • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06) 100 | 101 | [✓] IntelliJ IDEA Ultimate Edition (version 2018.1.1) 102 | • IntelliJ at /Applications/IntelliJ IDEA.app 103 | • Flutter plugin version 26.0.2 104 | • Dart plugin version 181.4096.12 105 | 106 | [✓] Connected devices (1 available) 107 | • MIX 2 • e4413f71 • android-arm64 • Android 8.0.0 (API 26) 108 | 109 | ! Doctor found issues in 1 category. 110 | ``` 111 | -------------------------------------------------------------------------------- /lib/zhihu/themeListPage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:transparent_image/transparent_image.dart'; 6 | import 'package:zhihudaily/common/common_divider.dart'; 7 | import 'package:zhihudaily/common/common_snakeBar.dart'; 8 | import 'package:zhihudaily/common/constant.dart'; 9 | import 'package:zhihudaily/model/base_model.dart'; 10 | import 'package:zhihudaily/model/theme_list_model.dart'; 11 | import 'package:zhihudaily/presenter/theme_list_presenter.dart'; 12 | import 'package:zhihudaily/presenter/theme_list_presenter_impl.dart'; 13 | import 'package:zhihudaily/utils/RouterUtils.dart'; 14 | import 'package:zhihudaily/widget/drawerContent.dart'; 15 | 16 | class ThemeListPage extends StatefulWidget { 17 | final String themeId; 18 | 19 | ThemeListPage(this.themeId, {Key key}) : super(key: key); 20 | 21 | @override 22 | _ThemeListPageState createState() { 23 | _ThemeListPageState view = new _ThemeListPageState(); 24 | ThemeListPresenter presenter = new ThemeListPresenterImpl(view); 25 | presenter.init(); 26 | return view; 27 | } 28 | } 29 | 30 | enum AppBarBehavior { normal, pinned, floating, snapping } 31 | 32 | class _ThemeListPageState extends State 33 | implements ThemeListView { 34 | final GlobalKey _refreshIndicatorKey = 35 | new GlobalKey(); 36 | 37 | // static final GlobalKey _scaffoldKey = new GlobalKey< 38 | // ScaffoldState>(); 39 | 40 | final double _appBarHeight = 256.0; 41 | 42 | AppBarBehavior _appBarBehavior = AppBarBehavior.pinned; 43 | 44 | String _title = Constant.themeTitle; 45 | 46 | ScrollController _scrollController; 47 | 48 | ThemeListPresenter _themeListPresenter; 49 | 50 | List _normalDatas = []; 51 | 52 | List _editorDatas = []; 53 | 54 | List _widgets = []; 55 | 56 | String _barBg = Constant.defBg; 57 | 58 | ThemeListModel _themeListModel; 59 | 60 | bool _isSlideUp = false; 61 | 62 | int _curStoryId; 63 | 64 | void _scrollListener() { 65 | //滑到最底部刷新 66 | if (_scrollController.position.pixels == 67 | _scrollController.position.maxScrollExtent) { 68 | _loadData(); 69 | } 70 | } 71 | 72 | Future _refreshData() { 73 | _isSlideUp = false; 74 | 75 | final Completer completer = new Completer(); 76 | 77 | _themeListPresenter.loadThemeList(widget.themeId, null); 78 | 79 | completer.complete(null); 80 | 81 | return completer.future; 82 | } 83 | 84 | Future _loadData() { 85 | _isSlideUp = true; 86 | 87 | final Completer completer = new Completer(); 88 | 89 | _themeListPresenter.loadThemeList(widget.themeId, '$_curStoryId'); 90 | 91 | setState(() {}); 92 | 93 | completer.complete(null); 94 | 95 | return completer.future; 96 | } 97 | 98 | @override 99 | void initState() { 100 | super.initState(); 101 | _scrollController = new ScrollController()..addListener(_scrollListener); 102 | _refreshData(); 103 | } 104 | 105 | @override 106 | void dispose() { 107 | super.dispose(); 108 | _scrollController.removeListener(_scrollListener); 109 | } 110 | 111 | Widget _buildEditor() { 112 | //横向控件的集合 113 | List editors = []; 114 | 115 | //主编 116 | Widget lableWidget = new Padding( 117 | padding: const EdgeInsets.only(right: 12.0), 118 | child: new Text( 119 | '主编', 120 | style: new TextStyle(fontSize: 14.0), 121 | ), 122 | ); 123 | 124 | editors.add(lableWidget); 125 | 126 | //循环加入主编的头像 127 | for (ThemeListEditorsModel model in _editorDatas) { 128 | Widget headView = new InkWell( 129 | onTap: () { 130 | //todo 131 | // RouterUtils.route2Web(context, model.name, model.url); 132 | }, 133 | child: new Padding( 134 | padding: const EdgeInsets.only( 135 | left: 6.0, right: 6.0, top: 12.0, bottom: 12.0), 136 | child: new CircleAvatar( 137 | radius: 12.0, 138 | backgroundImage: new NetworkImage(model.avatar), 139 | )), 140 | ); 141 | editors.add(headView); 142 | } 143 | 144 | //组装 145 | return new Column( 146 | children: [ 147 | new Padding( 148 | padding: const EdgeInsets.only(left: 12.0, right: 12.0), 149 | child: new Row( 150 | children: editors, 151 | ), 152 | ), 153 | CommonDivider.buildDivider(), 154 | ], 155 | ); 156 | } 157 | 158 | Widget _buildNormalItem(ThemeListStoriesModel item) { 159 | final List images = item.images; 160 | final String title = item.title; 161 | final int id = item.id; 162 | bool hasImage = (null != images && images.isNotEmpty); 163 | 164 | if (hasImage) { 165 | return new InkWell( 166 | onTap: () { 167 | RouterUtils.startWebView(context, id); 168 | }, 169 | child: new Padding( 170 | padding: const EdgeInsets.only(left: 12.0, right: 12.0), 171 | child: new SizedBox( 172 | height: Constant.normalItemHeight, 173 | child: new Column( 174 | children: [ 175 | new Row( 176 | children: [ 177 | new Expanded( 178 | child: new Text( 179 | title, 180 | style: new TextStyle( 181 | fontSize: 16.0, fontWeight: FontWeight.w300), 182 | ), 183 | ), 184 | new Padding( 185 | padding: const EdgeInsets.all(8.0), 186 | child: new SizedBox( 187 | height: 80.0, 188 | width: 80.0, 189 | child: new Image.network(images[0]), 190 | ), 191 | ) 192 | ], 193 | ), 194 | new Expanded( 195 | child: new Align( 196 | alignment: Alignment.bottomCenter, 197 | child: CommonDivider.buildDivider(), 198 | ), 199 | ), 200 | ], 201 | ), 202 | ))); 203 | } else { 204 | return new InkWell( 205 | onTap: () { 206 | RouterUtils.startWebView(context, id); 207 | }, 208 | child: new Padding( 209 | padding: const EdgeInsets.only(left: 12.0, right: 12.0), 210 | child: new SizedBox( 211 | height: Constant.normalItemHeight, 212 | child: new Column( 213 | children: [ 214 | new Row( 215 | children: [ 216 | new Expanded( 217 | child: new SizedBox( 218 | height: Constant.normalItemHeight, 219 | child: new Align( 220 | alignment: Alignment.centerLeft, 221 | child: new Text( 222 | title, 223 | style: new TextStyle( 224 | fontSize: 16.0, 225 | fontWeight: FontWeight.w300), 226 | ), 227 | ), 228 | ), 229 | ), 230 | ], 231 | ), 232 | new Expanded( 233 | child: new Align( 234 | alignment: Alignment.bottomCenter, 235 | child: CommonDivider.buildDivider(), 236 | ), 237 | ), 238 | ], 239 | ), 240 | ))); 241 | } 242 | } 243 | 244 | Widget _buildNewItem(ThemeListStoriesModel item) { 245 | Widget widget; 246 | 247 | switch (item.itemType) { 248 | case ThemeListStoriesModel.itemTypeEditor: 249 | widget = _buildEditor(); 250 | break; 251 | case ThemeListStoriesModel.itemTypeNormal: 252 | widget = _buildNormalItem(item); 253 | break; 254 | } 255 | return widget; 256 | } 257 | 258 | _refreshItems() { 259 | for (ThemeListStoriesModel model in _normalDatas) { 260 | _widgets.add(_buildNewItem(model)); 261 | } 262 | 263 | setState(() {}); 264 | } 265 | 266 | Widget _buildList(BuildContext context) { 267 | var content = new CustomScrollView( 268 | //没有铺满也可以滑动 269 | physics: AlwaysScrollableScrollPhysics(), 270 | controller: _scrollController, 271 | slivers: [ 272 | new SliverAppBar( 273 | expandedHeight: _appBarHeight, 274 | pinned: _appBarBehavior == AppBarBehavior.pinned, 275 | floating: _appBarBehavior == AppBarBehavior.floating || 276 | _appBarBehavior == AppBarBehavior.snapping, 277 | snap: _appBarBehavior == AppBarBehavior.snapping, 278 | flexibleSpace: new FlexibleSpaceBar( 279 | //标题 280 | title: Text('$_title'), 281 | centerTitle: true, 282 | //背景图 283 | background: new FadeInImage.memoryNetwork( 284 | placeholder: kTransparentImage, 285 | image: _barBg, 286 | fit: BoxFit.fitHeight), 287 | ), 288 | ), 289 | new SliverList( 290 | delegate: new SliverChildListDelegate( 291 | new List.generate(_normalDatas.length, (int i) { 292 | return _buildNewItem(_normalDatas[i]); 293 | })), 294 | ), 295 | ], 296 | ); 297 | 298 | var _refreshIndicator = new RefreshIndicator( 299 | key: _refreshIndicatorKey, 300 | onRefresh: _refreshData, 301 | child: content, 302 | ); 303 | 304 | return _refreshIndicator; 305 | } 306 | 307 | @override 308 | Widget build(BuildContext context) { 309 | return new Scaffold( 310 | // key: _scaffoldKey, 311 | backgroundColor: Colors.white, 312 | drawer: new Drawer( 313 | child: new DrawerPage(), 314 | ), 315 | body: _buildList(context), 316 | ); 317 | } 318 | 319 | @override 320 | setPresenter(ThemeListPresenter presenter) { 321 | _themeListPresenter = presenter; 322 | } 323 | 324 | @override 325 | void onLoadThemeListFail() { 326 | // TODO: implement onLoadThemeListFail 327 | } 328 | 329 | @override 330 | void onLoadThemeListSuc(BaseModel model) { 331 | if (!mounted) return; //异步处理,防止报错 332 | 333 | if (model.code != HttpStatus.OK) { 334 | CommonSnakeBar.buildSnakeBar(context, model.errorMsg); 335 | return; 336 | } 337 | 338 | if (_isSlideUp) { 339 | List normalList = model.data.stories; 340 | _normalDatas.addAll(normalList); 341 | } else { 342 | _themeListModel = model.data; 343 | List normalList = model.data.stories; 344 | List editorList = _themeListModel.editors; 345 | 346 | _themeListModel = model.data; 347 | 348 | _barBg = _themeListModel.image; 349 | 350 | _title = _themeListModel.name; 351 | 352 | _normalDatas.clear(); 353 | _editorDatas.clear(); 354 | 355 | _normalDatas = normalList; 356 | _editorDatas = editorList; 357 | 358 | _curStoryId = _normalDatas[0].id; 359 | 360 | if (null != _editorDatas && _editorDatas.isNotEmpty) { 361 | ThemeListStoriesModel fakeItem = new ThemeListStoriesModel(); 362 | fakeItem.setItemType(ThemeListStoriesModel.itemTypeEditor); 363 | _normalDatas.insert(0, fakeItem); 364 | } 365 | 366 | } 367 | 368 | _refreshItems(); 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 14 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 15 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 16 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 17 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 18 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 19 | 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; }; 20 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 21 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 22 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 23 | EB0651998627A6BC08E57EBD /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F21928A2C461EE664C8DA388 /* Pods_Runner.framework */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXCopyFilesBuildPhase section */ 27 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 28 | isa = PBXCopyFilesBuildPhase; 29 | buildActionMask = 2147483647; 30 | dstPath = ""; 31 | dstSubfolderSpec = 10; 32 | files = ( 33 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 34 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, 35 | ); 36 | name = "Embed Frameworks"; 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXCopyFilesBuildPhase section */ 40 | 41 | /* Begin PBXFileReference section */ 42 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 43 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 44 | 2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; }; 45 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 46 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 47 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 48 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 49 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 50 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 51 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 52 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 53 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 55 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 56 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 57 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | F21928A2C461EE664C8DA388 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | /* End PBXFileReference section */ 60 | 61 | /* Begin PBXFrameworksBuildPhase section */ 62 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 67 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 68 | EB0651998627A6BC08E57EBD /* Pods_Runner.framework in Frameworks */, 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXFrameworksBuildPhase section */ 73 | 74 | /* Begin PBXGroup section */ 75 | 0C6D914149C756CDE70B051B /* Frameworks */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | F21928A2C461EE664C8DA388 /* Pods_Runner.framework */, 79 | ); 80 | name = Frameworks; 81 | sourceTree = ""; 82 | }; 83 | 34AF4E4C77695DC132C6AEDF /* Pods */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | ); 87 | name = Pods; 88 | sourceTree = ""; 89 | }; 90 | 9740EEB11CF90186004384FC /* Flutter */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 2D5378251FAA1A9400D5DBA9 /* flutter_assets */, 94 | 3B80C3931E831B6300D905FE /* App.framework */, 95 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 96 | 9740EEBA1CF902C7004384FC /* Flutter.framework */, 97 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 98 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 99 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 100 | ); 101 | name = Flutter; 102 | sourceTree = ""; 103 | }; 104 | 97C146E51CF9000F007C117D = { 105 | isa = PBXGroup; 106 | children = ( 107 | 9740EEB11CF90186004384FC /* Flutter */, 108 | 97C146F01CF9000F007C117D /* Runner */, 109 | 97C146EF1CF9000F007C117D /* Products */, 110 | 34AF4E4C77695DC132C6AEDF /* Pods */, 111 | 0C6D914149C756CDE70B051B /* Frameworks */, 112 | ); 113 | sourceTree = ""; 114 | }; 115 | 97C146EF1CF9000F007C117D /* Products */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 97C146EE1CF9000F007C117D /* Runner.app */, 119 | ); 120 | name = Products; 121 | sourceTree = ""; 122 | }; 123 | 97C146F01CF9000F007C117D /* Runner */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 127 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 128 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 129 | 97C147021CF9000F007C117D /* Info.plist */, 130 | 97C146F11CF9000F007C117D /* Supporting Files */, 131 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 132 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 133 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 134 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 135 | ); 136 | path = Runner; 137 | sourceTree = ""; 138 | }; 139 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | ); 143 | name = "Supporting Files"; 144 | sourceTree = ""; 145 | }; 146 | /* End PBXGroup section */ 147 | 148 | /* Begin PBXNativeTarget section */ 149 | 97C146ED1CF9000F007C117D /* Runner */ = { 150 | isa = PBXNativeTarget; 151 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 152 | buildPhases = ( 153 | E3A3C064A2A1150EAB121034 /* [CP] Check Pods Manifest.lock */, 154 | 9740EEB61CF901F6004384FC /* Run Script */, 155 | 97C146EA1CF9000F007C117D /* Sources */, 156 | 97C146EB1CF9000F007C117D /* Frameworks */, 157 | 97C146EC1CF9000F007C117D /* Resources */, 158 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 159 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 160 | CAA558AFB9BBA78D9B607F6A /* [CP] Embed Pods Frameworks */, 161 | ); 162 | buildRules = ( 163 | ); 164 | dependencies = ( 165 | ); 166 | name = Runner; 167 | productName = Runner; 168 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 169 | productType = "com.apple.product-type.application"; 170 | }; 171 | /* End PBXNativeTarget section */ 172 | 173 | /* Begin PBXProject section */ 174 | 97C146E61CF9000F007C117D /* Project object */ = { 175 | isa = PBXProject; 176 | attributes = { 177 | LastUpgradeCheck = 0910; 178 | ORGANIZATIONNAME = "The Chromium Authors"; 179 | TargetAttributes = { 180 | 97C146ED1CF9000F007C117D = { 181 | CreatedOnToolsVersion = 7.3.1; 182 | LastSwiftMigration = 0910; 183 | }; 184 | }; 185 | }; 186 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 187 | compatibilityVersion = "Xcode 3.2"; 188 | developmentRegion = English; 189 | hasScannedForEncodings = 0; 190 | knownRegions = ( 191 | en, 192 | Base, 193 | ); 194 | mainGroup = 97C146E51CF9000F007C117D; 195 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 196 | projectDirPath = ""; 197 | projectRoot = ""; 198 | targets = ( 199 | 97C146ED1CF9000F007C117D /* Runner */, 200 | ); 201 | }; 202 | /* End PBXProject section */ 203 | 204 | /* Begin PBXResourcesBuildPhase section */ 205 | 97C146EC1CF9000F007C117D /* Resources */ = { 206 | isa = PBXResourcesBuildPhase; 207 | buildActionMask = 2147483647; 208 | files = ( 209 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 210 | 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */, 211 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 212 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 213 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 214 | 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */, 215 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 216 | ); 217 | runOnlyForDeploymentPostprocessing = 0; 218 | }; 219 | /* End PBXResourcesBuildPhase section */ 220 | 221 | /* Begin PBXShellScriptBuildPhase section */ 222 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 223 | isa = PBXShellScriptBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | ); 227 | inputPaths = ( 228 | ); 229 | name = "Thin Binary"; 230 | outputPaths = ( 231 | ); 232 | runOnlyForDeploymentPostprocessing = 0; 233 | shellPath = /bin/sh; 234 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; 235 | }; 236 | 9740EEB61CF901F6004384FC /* Run Script */ = { 237 | isa = PBXShellScriptBuildPhase; 238 | buildActionMask = 2147483647; 239 | files = ( 240 | ); 241 | inputPaths = ( 242 | ); 243 | name = "Run Script"; 244 | outputPaths = ( 245 | ); 246 | runOnlyForDeploymentPostprocessing = 0; 247 | shellPath = /bin/sh; 248 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 249 | }; 250 | CAA558AFB9BBA78D9B607F6A /* [CP] Embed Pods Frameworks */ = { 251 | isa = PBXShellScriptBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | ); 255 | inputPaths = ( 256 | "${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", 257 | "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", 258 | "${BUILT_PRODUCTS_DIR}/flutter_webview_plugin/flutter_webview_plugin.framework", 259 | ); 260 | name = "[CP] Embed Pods Frameworks"; 261 | outputPaths = ( 262 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", 263 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_webview_plugin.framework", 264 | ); 265 | runOnlyForDeploymentPostprocessing = 0; 266 | shellPath = /bin/sh; 267 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 268 | showEnvVarsInLog = 0; 269 | }; 270 | E3A3C064A2A1150EAB121034 /* [CP] Check Pods Manifest.lock */ = { 271 | isa = PBXShellScriptBuildPhase; 272 | buildActionMask = 2147483647; 273 | files = ( 274 | ); 275 | inputPaths = ( 276 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 277 | "${PODS_ROOT}/Manifest.lock", 278 | ); 279 | name = "[CP] Check Pods Manifest.lock"; 280 | outputPaths = ( 281 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 282 | ); 283 | runOnlyForDeploymentPostprocessing = 0; 284 | shellPath = /bin/sh; 285 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 286 | showEnvVarsInLog = 0; 287 | }; 288 | /* End PBXShellScriptBuildPhase section */ 289 | 290 | /* Begin PBXSourcesBuildPhase section */ 291 | 97C146EA1CF9000F007C117D /* Sources */ = { 292 | isa = PBXSourcesBuildPhase; 293 | buildActionMask = 2147483647; 294 | files = ( 295 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 296 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 297 | ); 298 | runOnlyForDeploymentPostprocessing = 0; 299 | }; 300 | /* End PBXSourcesBuildPhase section */ 301 | 302 | /* Begin PBXVariantGroup section */ 303 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 304 | isa = PBXVariantGroup; 305 | children = ( 306 | 97C146FB1CF9000F007C117D /* Base */, 307 | ); 308 | name = Main.storyboard; 309 | sourceTree = ""; 310 | }; 311 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 312 | isa = PBXVariantGroup; 313 | children = ( 314 | 97C147001CF9000F007C117D /* Base */, 315 | ); 316 | name = LaunchScreen.storyboard; 317 | sourceTree = ""; 318 | }; 319 | /* End PBXVariantGroup section */ 320 | 321 | /* Begin XCBuildConfiguration section */ 322 | 97C147031CF9000F007C117D /* Debug */ = { 323 | isa = XCBuildConfiguration; 324 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 325 | buildSettings = { 326 | ALWAYS_SEARCH_USER_PATHS = NO; 327 | CLANG_ANALYZER_NONNULL = YES; 328 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 329 | CLANG_CXX_LIBRARY = "libc++"; 330 | CLANG_ENABLE_MODULES = YES; 331 | CLANG_ENABLE_OBJC_ARC = YES; 332 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 333 | CLANG_WARN_BOOL_CONVERSION = YES; 334 | CLANG_WARN_COMMA = YES; 335 | CLANG_WARN_CONSTANT_CONVERSION = YES; 336 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 337 | CLANG_WARN_EMPTY_BODY = YES; 338 | CLANG_WARN_ENUM_CONVERSION = YES; 339 | CLANG_WARN_INFINITE_RECURSION = YES; 340 | CLANG_WARN_INT_CONVERSION = YES; 341 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 342 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 343 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 344 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 345 | CLANG_WARN_STRICT_PROTOTYPES = YES; 346 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 347 | CLANG_WARN_UNREACHABLE_CODE = YES; 348 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 349 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 350 | COPY_PHASE_STRIP = NO; 351 | DEBUG_INFORMATION_FORMAT = dwarf; 352 | ENABLE_STRICT_OBJC_MSGSEND = YES; 353 | ENABLE_TESTABILITY = YES; 354 | GCC_C_LANGUAGE_STANDARD = gnu99; 355 | GCC_DYNAMIC_NO_PIC = NO; 356 | GCC_NO_COMMON_BLOCKS = YES; 357 | GCC_OPTIMIZATION_LEVEL = 0; 358 | GCC_PREPROCESSOR_DEFINITIONS = ( 359 | "DEBUG=1", 360 | "$(inherited)", 361 | ); 362 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 363 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 364 | GCC_WARN_UNDECLARED_SELECTOR = YES; 365 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 366 | GCC_WARN_UNUSED_FUNCTION = YES; 367 | GCC_WARN_UNUSED_VARIABLE = YES; 368 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 369 | MTL_ENABLE_DEBUG_INFO = YES; 370 | ONLY_ACTIVE_ARCH = YES; 371 | SDKROOT = iphoneos; 372 | TARGETED_DEVICE_FAMILY = "1,2"; 373 | }; 374 | name = Debug; 375 | }; 376 | 97C147041CF9000F007C117D /* Release */ = { 377 | isa = XCBuildConfiguration; 378 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 379 | buildSettings = { 380 | ALWAYS_SEARCH_USER_PATHS = NO; 381 | CLANG_ANALYZER_NONNULL = YES; 382 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 383 | CLANG_CXX_LIBRARY = "libc++"; 384 | CLANG_ENABLE_MODULES = YES; 385 | CLANG_ENABLE_OBJC_ARC = YES; 386 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 387 | CLANG_WARN_BOOL_CONVERSION = YES; 388 | CLANG_WARN_COMMA = YES; 389 | CLANG_WARN_CONSTANT_CONVERSION = YES; 390 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 391 | CLANG_WARN_EMPTY_BODY = YES; 392 | CLANG_WARN_ENUM_CONVERSION = YES; 393 | CLANG_WARN_INFINITE_RECURSION = YES; 394 | CLANG_WARN_INT_CONVERSION = YES; 395 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 396 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 397 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 398 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 399 | CLANG_WARN_STRICT_PROTOTYPES = YES; 400 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 401 | CLANG_WARN_UNREACHABLE_CODE = YES; 402 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 403 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 404 | COPY_PHASE_STRIP = NO; 405 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 406 | ENABLE_NS_ASSERTIONS = NO; 407 | ENABLE_STRICT_OBJC_MSGSEND = YES; 408 | GCC_C_LANGUAGE_STANDARD = gnu99; 409 | GCC_NO_COMMON_BLOCKS = YES; 410 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 411 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 412 | GCC_WARN_UNDECLARED_SELECTOR = YES; 413 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 414 | GCC_WARN_UNUSED_FUNCTION = YES; 415 | GCC_WARN_UNUSED_VARIABLE = YES; 416 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 417 | MTL_ENABLE_DEBUG_INFO = NO; 418 | SDKROOT = iphoneos; 419 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 420 | TARGETED_DEVICE_FAMILY = "1,2"; 421 | VALIDATE_PRODUCT = YES; 422 | }; 423 | name = Release; 424 | }; 425 | 97C147061CF9000F007C117D /* Debug */ = { 426 | isa = XCBuildConfiguration; 427 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 428 | buildSettings = { 429 | ARCHS = arm64; 430 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 431 | CLANG_ENABLE_MODULES = YES; 432 | ENABLE_BITCODE = NO; 433 | FRAMEWORK_SEARCH_PATHS = ( 434 | "$(inherited)", 435 | "$(PROJECT_DIR)/Flutter", 436 | ); 437 | INFOPLIST_FILE = Runner/Info.plist; 438 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 439 | LIBRARY_SEARCH_PATHS = ( 440 | "$(inherited)", 441 | "$(PROJECT_DIR)/Flutter", 442 | ); 443 | PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.starter; 444 | PRODUCT_NAME = "$(TARGET_NAME)"; 445 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 446 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 447 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 448 | SWIFT_VERSION = 4.0; 449 | }; 450 | name = Debug; 451 | }; 452 | 97C147071CF9000F007C117D /* Release */ = { 453 | isa = XCBuildConfiguration; 454 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 455 | buildSettings = { 456 | ARCHS = arm64; 457 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 458 | CLANG_ENABLE_MODULES = YES; 459 | ENABLE_BITCODE = NO; 460 | FRAMEWORK_SEARCH_PATHS = ( 461 | "$(inherited)", 462 | "$(PROJECT_DIR)/Flutter", 463 | ); 464 | INFOPLIST_FILE = Runner/Info.plist; 465 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 466 | LIBRARY_SEARCH_PATHS = ( 467 | "$(inherited)", 468 | "$(PROJECT_DIR)/Flutter", 469 | ); 470 | PRODUCT_BUNDLE_IDENTIFIER = com.yourcompany.starter; 471 | PRODUCT_NAME = "$(TARGET_NAME)"; 472 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 473 | SWIFT_SWIFT3_OBJC_INFERENCE = On; 474 | SWIFT_VERSION = 4.0; 475 | }; 476 | name = Release; 477 | }; 478 | /* End XCBuildConfiguration section */ 479 | 480 | /* Begin XCConfigurationList section */ 481 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 482 | isa = XCConfigurationList; 483 | buildConfigurations = ( 484 | 97C147031CF9000F007C117D /* Debug */, 485 | 97C147041CF9000F007C117D /* Release */, 486 | ); 487 | defaultConfigurationIsVisible = 0; 488 | defaultConfigurationName = Release; 489 | }; 490 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 491 | isa = XCConfigurationList; 492 | buildConfigurations = ( 493 | 97C147061CF9000F007C117D /* Debug */, 494 | 97C147071CF9000F007C117D /* Release */, 495 | ); 496 | defaultConfigurationIsVisible = 0; 497 | defaultConfigurationName = Release; 498 | }; 499 | /* End XCConfigurationList section */ 500 | }; 501 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 502 | } 503 | --------------------------------------------------------------------------------