├── .gitignore ├── Archive.zip ├── README.md ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── lgyw │ │ │ └── flutterapp │ │ │ └── MainActivity.java │ │ └── res │ │ ├── drawable │ │ └── launch_background.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── demonstrationgif ├── 20180814_142135.gif ├── 20180814_142220.gif ├── 20180821_094948.gif ├── 20180912_100745.gif ├── 20181023_90359.gif ├── 20181127_165032.gif ├── 20181130_092350.gif ├── 20181207_155551.gif ├── 20181229_155650.gif └── screenshot_20190127113535.png ├── images ├── chaonan1.jpeg ├── chaonan2.jpg ├── chaonan3.jpeg ├── chaonan4.jpeg ├── close.png ├── icon_arrow.png ├── icon_cry.png ├── refresh.png └── tianmao.jpg ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── main.m ├── lib ├── Home.dart ├── bin │ ├── adsorptiondatabin.dart │ ├── adsorptionlistbin.dart │ ├── appinfobin.dart │ ├── bubblebin.dart │ ├── dragablegridviewbin.dart │ └── gridviewitembin.dart ├── components │ ├── adsorptionview │ │ ├── adsorptionview.dart │ │ ├── equalHeightState.dart │ │ └── notEqualHeightState.dart │ ├── dragablegridview.dart │ ├── drawablestarttext.dart │ ├── loginanimation.dart │ ├── marquee.dart │ ├── pulltorefresh.dart │ ├── radarchart.dart │ └── waveprogressbar.dart ├── main.dart └── ui │ ├── firstpage │ ├── adsorptionviewdemo.dart │ ├── animations.dart │ ├── dragablegridviewdemo.dart │ ├── firstPage.dart │ ├── marqueedemo.dart │ └── pulltorefreshdemo.dart │ ├── secondpage │ ├── beziercurvedemo.dart │ ├── blendmode.dart │ ├── bubbles.dart │ ├── dashboard.dart │ ├── drawablestarttext.dart │ ├── loginanimdemo.dart │ ├── radarchartdemo.dart │ ├── secondPage.dart │ └── timepicker.dart │ └── thirdpage │ ├── cutScreen.dart │ ├── examples │ ├── CameraTest.dart │ ├── Examples.dart │ └── ImagePicker.dart │ ├── home_page.dart │ ├── shakeView.dart │ ├── testFile.dart │ └── thirdPage.dart ├── pictures └── Dragable gridview.png ├── pubspec.lock ├── pubspec.yaml └── test └── widget_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | build/ 8 | 9 | .flutter-plugins 10 | .idea 11 | .metadata 12 | *.iml -------------------------------------------------------------------------------- /Archive.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/Archive.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `由于GIF太多(大),演示的图片可能会卡,请移步至demonstrationgif文件夹下可查看单个GIF图片` 2 | 3 | ## PullToRefresh [![pub package](https://img.shields.io/pub/v/pulltorefresh_flutter.svg)](https://pub.dartlang.org/packages/pulltorefresh_flutter) 4 | #### Usage 5 | Add this to your package's pubspec.yaml file: 6 | 7 | dependencies: 8 | pulltorefresh_flutter: "^0.1.9" 9 | 10 | If you want to use the default refresh image of this project (the rotated image), please download https://raw.githubusercontent.com/baoolong/PullToRefresh_Flutter/master/images/refresh.png to your images folder, and Pubspec.yaml is declared as follows. 11 | 12 | If you want to use other images, put the image in the Images folder, declare it in the Pubspec.yaml file, and add the property refreshIconPath in the PullAndPush class. 13 | 14 | assets: 15 | - images/refresh.png 16 | 17 | Add it to your dart file: 18 | 19 | import 'package:pulltorefresh_flutter/pulltorefresh_flutter.dart'; 20 | #### Example 21 | [https://github.com/baoolong/PullToRefresh_Flutter](https://github.com/baoolong/PullToRefresh_Flutter) 22 | 23 | 本功能只实现基本的上下拉刷新,可在这个基础上进行改进、优化、封装,如果只是使用,可在build方法中修改ListView控件和List数组的泛型,已经兼容IOS,已经支持对下拉和上拉的分别控制 24 | 25 | 26 | 27 | ## 仿京东广告滑动切换 ## 28 | 模仿的京东潮男模块的广告滑动切换,本人做的比较粗糙,大家可以在此基础上改进,比如滞后滑动,底层图片缩小等,由于没有进行屏幕适配,所以可能不同的手机会显示很丑,这是由于我在设计图片之间的Magin是用屏幕宽度减去两边距屏幕的宽度,再除以3计算的,大家可以根据需要去设定图片之间的Magin,最好固定值 29 | 30 | 31 | 32 | ## Marquee(跑马灯) [![pub package](https://img.shields.io/pub/v/marquee_flutter.svg)](https://pub.dartlang.org/packages/marquee_flutter) 33 | 34 | 一个用ListView做的跑马灯,可以垂直方向滚动,也可以水平方向滚动 35 | 36 | A Marquee widght with ListView,Can scroll vertically or horizontally 37 | 38 | #### Usage 39 | Add this to your package's pubspec.yaml file: 40 | 41 | dependencies: 42 | marquee_flutter: ^0.1.3 43 | 44 | Add it to your dart file: 45 | 46 | import 'package:marquee_flutter/marquee_flutter.dart'; 47 | 48 | #### Example 49 | [https://github.com/baoolong/MarqueeWidget](https://github.com/baoolong/MarqueeWidget) 50 | 51 | 52 | 采用ListView绘制,将ListView设置为不可手动滑动,然后启动Timer来回拖动,造成跑马灯的错觉 53 | 54 | ## DragableGridView [![pub package](https://img.shields.io/pub/v/dragablegridview_flutter.svg)](https://pub.dartlang.org/packages/dragablegridview_flutter) 55 | 采用GridView +OverflowBox +Container的transform属性来完成,基本稳定,现在加入了删除功能和动画,后续持续改进,代码会越来越精简,逻辑会更清晰,学习的朋友可以拿去自己研究改进,添加新功能,下面是示例图 56 | ### Usage 57 | 58 | Add this to your package's pubspec.yaml file: 59 | 60 | dependencies: 61 | dragablegridview_flutter: ^0.2.2 62 | 63 | Add it to your dart file: 64 | 65 | import 'package:dragablegridview_flutter/dragablegridview_flutter.dart'; 66 | 67 | And GridView dataBin must extends DragAbleGridViewBin ,Add it to your dataBin file 68 | 69 | import 'package:dragablegridview_flutter/dragablegridviewbin.dart'; 70 | 71 | ### Example 72 | [https://github.com/baoolong/DragableGridview](https://github.com/baoolong/DragableGridview) 73 | 74 | ---------- 75 | 76 | 77 | 78 | 79 | ## AdsorptionView [![pub package](https://img.shields.io/pub/v/adsorptionview_flutter.svg)](https://pub.dartlang.org/packages/adsorptionview_flutter) 80 | ListView吸顶控件,本控件只适用于ListView的Item高度固定的布局(AdsorptionViewState),如果高度不固定会有偏差。已经更新了非固定高度的吸顶布局(AdsorptionViewNotEqualHeightState),有一点点小问题,请自行解决,里面有如何获取ListView第一个可见Item的方法,可供参考 81 | ### Usage 82 | Add this to your package's pubspec.yaml file: 83 | 84 | dependencies: 85 | adsorptionview_flutter: ^0.1.3 86 | 87 | Add it to your dart file: 88 | 89 | import 'package:adsorptionview_flutter/adsorptionview_flutter.dart'; 90 | 91 | And ListView dataBin must extends AdsorptionData ,Add it to your dataBin file 92 | 93 | import 'package:adsorptionview_flutter/adsorptiondatabin.dart'; 94 | 95 | ### Example 96 | [https://github.com/baoolong/Adsorptionview](https://github.com/baoolong/Adsorptionview) 97 | 98 | 99 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | android { 18 | compileSdkVersion 28 19 | 20 | lintOptions { 21 | disable 'InvalidPackage' 22 | } 23 | 24 | defaultConfig { 25 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 26 | applicationId "com.lgyw.flutterapp" 27 | minSdkVersion 21 28 | targetSdkVersion 28 29 | versionCode 1 30 | versionName "1.0" 31 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 32 | } 33 | 34 | buildTypes { 35 | release { 36 | // TODO: Add your own signing config for the release build. 37 | // Signing with the debug keys for now, so `flutter run --release` works. 38 | signingConfig signingConfigs.debug 39 | } 40 | } 41 | } 42 | 43 | flutter { 44 | source '../..' 45 | } 46 | 47 | dependencies { 48 | testImplementation 'junit:junit:4.12' 49 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 50 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 51 | } 52 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 15 | 19 | 26 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/lgyw/flutterapp/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.lgyw.flutterapp; 2 | 3 | import android.content.Context; 4 | import android.hardware.Sensor; 5 | import android.hardware.SensorEvent; 6 | import android.hardware.SensorEventListener; 7 | import android.hardware.SensorManager; 8 | import android.os.Bundle; 9 | import android.util.Log; 10 | 11 | import io.flutter.app.FlutterActivity; 12 | import io.flutter.plugin.common.MethodCall; 13 | import io.flutter.plugin.common.MethodChannel; 14 | import io.flutter.plugins.GeneratedPluginRegistrant; 15 | import io.flutter.plugin.common.MethodChannel.Result; 16 | 17 | public class MainActivity extends FlutterActivity { 18 | 19 | private static final String CHANNEL = "com.flutter.lgyw/sensor"; 20 | private SensorManager sm; 21 | private double pressure=0.0; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | GeneratedPluginRegistrant.registerWith(this); 27 | 28 | sm = (SensorManager) getSystemService(Context.SENSOR_SERVICE); 29 | 30 | new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler( 31 | new MethodChannel.MethodCallHandler() { 32 | @Override 33 | public void onMethodCall(MethodCall call, Result result) { 34 | if (call.method.equals("registerSensor")) { 35 | // 为压力传感器注册监听器 36 | boolean isSuccess=sm.registerListener(sensorEventListener, sm.getDefaultSensor(Sensor.TYPE_PRESSURE), SensorManager.SENSOR_DELAY_NORMAL); 37 | if(isSuccess){ 38 | result.success("Success"); 39 | }else{ 40 | result.success("Faile"); 41 | } 42 | }else if(call.method.equals("unRegisterSensor")){ 43 | sm.unregisterListener(sensorEventListener); 44 | result.success(""); 45 | }else if(call.method.equals("getPressure")){ 46 | result.success(pressure); 47 | } 48 | } 49 | }); 50 | } 51 | 52 | private SensorEventListener sensorEventListener=new SensorEventListener() { 53 | @Override 54 | public void onSensorChanged(SensorEvent sensorEvent) { 55 | if(sensorEvent.sensor.getType() == Sensor.TYPE_PRESSURE){ 56 | pressure = sensorEvent.values[0]; 57 | Log.i("MainActivity","sensorEvent.values[0] = " +sensorEvent.values[0]); 58 | } 59 | } 60 | 61 | @Override 62 | public void onAccuracyChanged(Sensor sensor, int accuracy) { 63 | 64 | } 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.3.2' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=http\://services.gradle.org/distributions/gradle-4.10.1-all.zip 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /demonstrationgif/20180814_142135.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/demonstrationgif/20180814_142135.gif -------------------------------------------------------------------------------- /demonstrationgif/20180814_142220.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/demonstrationgif/20180814_142220.gif -------------------------------------------------------------------------------- /demonstrationgif/20180821_094948.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/demonstrationgif/20180821_094948.gif -------------------------------------------------------------------------------- /demonstrationgif/20180912_100745.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/demonstrationgif/20180912_100745.gif -------------------------------------------------------------------------------- /demonstrationgif/20181023_90359.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/demonstrationgif/20181023_90359.gif -------------------------------------------------------------------------------- /demonstrationgif/20181127_165032.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/demonstrationgif/20181127_165032.gif -------------------------------------------------------------------------------- /demonstrationgif/20181130_092350.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/demonstrationgif/20181130_092350.gif -------------------------------------------------------------------------------- /demonstrationgif/20181207_155551.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/demonstrationgif/20181207_155551.gif -------------------------------------------------------------------------------- /demonstrationgif/20181229_155650.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/demonstrationgif/20181229_155650.gif -------------------------------------------------------------------------------- /demonstrationgif/screenshot_20190127113535.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/demonstrationgif/screenshot_20190127113535.png -------------------------------------------------------------------------------- /images/chaonan1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/images/chaonan1.jpeg -------------------------------------------------------------------------------- /images/chaonan2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/images/chaonan2.jpg -------------------------------------------------------------------------------- /images/chaonan3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/images/chaonan3.jpeg -------------------------------------------------------------------------------- /images/chaonan4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/images/chaonan4.jpeg -------------------------------------------------------------------------------- /images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/images/close.png -------------------------------------------------------------------------------- /images/icon_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/images/icon_arrow.png -------------------------------------------------------------------------------- /images/icon_cry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/images/icon_cry.png -------------------------------------------------------------------------------- /images/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/images/refresh.png -------------------------------------------------------------------------------- /images/tianmao.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/images/tianmao.jpg -------------------------------------------------------------------------------- /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 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/app.flx 37 | /Flutter/app.zip 38 | /Flutter/flutter_assets/ 39 | /Flutter/App.framework 40 | /Flutter/Flutter.framework 41 | /Flutter/Generated.xcconfig 42 | /ServiceDefinitions.json 43 | 44 | Pods/ 45 | .symlinks/ 46 | -------------------------------------------------------------------------------- /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 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | pods_ary = [] 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) { |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | pods_ary.push({:name => podname, :path => podpath}); 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | } 32 | return pods_ary 33 | end 34 | 35 | target 'Runner' do 36 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 37 | # referring to absolute paths on developers' machines. 38 | system('rm -rf .symlinks') 39 | system('mkdir -p .symlinks/plugins') 40 | 41 | # Flutter Pods 42 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') 43 | if generated_xcode_build_settings.empty? 44 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." 45 | end 46 | generated_xcode_build_settings.map { |p| 47 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR' 48 | symlink = File.join('.symlinks', 'flutter') 49 | File.symlink(File.dirname(p[:path]), symlink) 50 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) 51 | end 52 | } 53 | 54 | # Plugin Pods 55 | plugin_pods = parse_KV_file('../.flutter-plugins') 56 | plugin_pods.map { |p| 57 | symlink = File.join('.symlinks', 'plugins', p[:name]) 58 | File.symlink(p[:path], symlink) 59 | pod p[:name], :path => File.join(symlink, 'ios') 60 | } 61 | end 62 | 63 | post_install do |installer| 64 | installer.pods_project.targets.each do |target| 65 | target.build_configurations.each do |config| 66 | config.build_settings['ENABLE_BITCODE'] = 'NO' 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - camera (0.0.1): 3 | - Flutter 4 | - Flutter (1.0.0) 5 | - image_picker (0.0.1): 6 | - Flutter 7 | - path_provider (0.0.1): 8 | - Flutter 9 | - video_player (0.0.1): 10 | - Flutter 11 | 12 | DEPENDENCIES: 13 | - camera (from `.symlinks/plugins/camera/ios`) 14 | - Flutter (from `.symlinks/flutter/ios`) 15 | - image_picker (from `.symlinks/plugins/image_picker/ios`) 16 | - path_provider (from `.symlinks/plugins/path_provider/ios`) 17 | - video_player (from `.symlinks/plugins/video_player/ios`) 18 | 19 | EXTERNAL SOURCES: 20 | camera: 21 | :path: ".symlinks/plugins/camera/ios" 22 | Flutter: 23 | :path: ".symlinks/flutter/ios" 24 | image_picker: 25 | :path: ".symlinks/plugins/image_picker/ios" 26 | path_provider: 27 | :path: ".symlinks/plugins/path_provider/ios" 28 | video_player: 29 | :path: ".symlinks/plugins/video_player/ios" 30 | 31 | SPEC CHECKSUMS: 32 | camera: d56ad165545ae5a0ffb892376033760a969c68c8 33 | Flutter: 58dd7d1b27887414a370fcccb9e645c08ffd7a6a 34 | image_picker: 16e5fec1fbc87fd3b297c53e4048521eaf17cd06 35 | path_provider: f96fff6166a8867510d2c25fdcc346327cc4b259 36 | video_player: 3964090a33353060ed7f58aa6427c7b4b208ec21 37 | 38 | PODFILE CHECKSUM: aff02bfeed411c636180d6812254b2daeea14d09 39 | 40 | COCOAPODS: 1.6.1 41 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 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 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Original 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/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/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/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/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/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/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/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/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/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/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/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/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/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/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/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/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/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/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/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/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/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/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/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/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutterapp 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 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/Home.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/rendering.dart'; 4 | import 'package:flutterapp/ui/firstpage/firstPage.dart'; 5 | import 'package:flutterapp/ui/secondpage/secondPage.dart'; 6 | import 'package:flutterapp/ui/thirdpage/thirdPage.dart'; 7 | import 'dart:ui' as ui show Image; 8 | 9 | class MyHomePage extends StatefulWidget{ 10 | @override 11 | State createState() { 12 | return new _MyHomePageState(); 13 | } 14 | } 15 | 16 | 17 | class _MyHomePageState extends State with SingleTickerProviderStateMixin{ 18 | 19 | PageController controller; 20 | var currentPage=0; 21 | GlobalKey _globalKey=new GlobalKey(); 22 | 23 | @override 24 | void initState() { 25 | super.initState(); 26 | controller=new PageController(initialPage: this.currentPage); 27 | } 28 | 29 | @override 30 | void dispose() { 31 | controller.dispose(); 32 | super.dispose(); 33 | } 34 | 35 | 36 | Future _widgetToImage(final GlobalKey _globalKey) async { 37 | try { 38 | RenderRepaintBoundary boundary = _globalKey.currentContext 39 | .findRenderObject(); 40 | ui.Image image = await boundary.toImage(pixelRatio: 1.0); 41 | var recorder = new PictureRecorder(); 42 | Canvas canvas = Canvas(recorder); 43 | canvas.drawImage(image, new Offset(0.0, 0.0), new Paint()); 44 | Picture picture = recorder.endRecording(); 45 | return picture; 46 | }catch(e){ 47 | print(e); 48 | } 49 | return null; 50 | } 51 | 52 | 53 | @override 54 | Widget build(BuildContext context) { 55 | return new RepaintBoundary( 56 | key: _globalKey, 57 | child: new Scaffold( 58 | body: new PageView( 59 | children: [ 60 | new FirstPage(), 61 | new SecondPage(), 62 | new ThirdPage() 63 | ], 64 | controller: controller, 65 | onPageChanged: (int position){ 66 | _widgetToImage(_globalKey); 67 | setState(() { 68 | this.currentPage = position; 69 | }); 70 | }, 71 | ), 72 | bottomNavigationBar: 73 | new BottomNavigationBar(items: [ 74 | new BottomNavigationBarItem( 75 | icon: new Icon(Icons.home), 76 | title: new Title( 77 | title:"列表", 78 | color: Colors.white, 79 | child: new Text("列表",style: new TextStyle(fontSize: 13.0))) 80 | ), 81 | new BottomNavigationBarItem( 82 | icon: new Icon(Icons.message), 83 | title: new Title( 84 | title:"通知", 85 | color: Colors.white, 86 | child: new Text("通知",style: new TextStyle(fontSize: 13.0))) 87 | ), 88 | new BottomNavigationBarItem( 89 | icon: new Icon(Icons.cloud), 90 | title: new Title( 91 | title:"我的", 92 | color: Colors.white, 93 | child: new Text("我的",style: new TextStyle(fontSize: 13.0))) 94 | ) 95 | ], 96 | iconSize: 28.0, 97 | onTap: (int position){ 98 | setState(() { 99 | controller.animateToPage( 100 | position, duration: const Duration(milliseconds: 300), 101 | curve: Curves.ease); 102 | }); 103 | }, 104 | currentIndex: currentPage, 105 | fixedColor: Colors.blue, 106 | ) 107 | ), 108 | ); 109 | } 110 | } -------------------------------------------------------------------------------- /lib/bin/adsorptiondatabin.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AdsorptionData{ 4 | bool isHeader=false; 5 | GlobalKey adsorptionKey=new GlobalKey(); 6 | double headerPosition = -99.0; 7 | } -------------------------------------------------------------------------------- /lib/bin/adsorptionlistbin.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutterapp/bin/adsorptiondatabin.dart'; 2 | 3 | class AdsorptionListBin extends AdsorptionData{ 4 | AdsorptionListBin( this.headerName); 5 | 6 | String headerName; 7 | 8 | @override 9 | String toString() { 10 | return 'AdsorptionListBin{headerName: $headerName}'; 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /lib/bin/appinfobin.dart: -------------------------------------------------------------------------------- 1 | 2 | class AppInfoBin{ 3 | AppInfoBin(this.title,this.times,this.content,this.isTapDown); 4 | String title; 5 | String times; 6 | String content; 7 | bool isTapDown; 8 | } -------------------------------------------------------------------------------- /lib/bin/bubblebin.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class BubbleBin { 4 | double radius; 5 | double speed; 6 | double disappearHeight; 7 | Color color; 8 | int transparency; 9 | double xPosition; 10 | double yPosition; 11 | 12 | BubbleBin({ 13 | @required this.radius, 14 | @required this.speed, 15 | @required this.disappearHeight, 16 | @required this.color, 17 | @required this.transparency, 18 | @required this.xPosition, 19 | @required this.yPosition 20 | }); 21 | 22 | @override 23 | String toString() { 24 | return 'BubbleBin{radius: $radius, speed: $speed, disappearHeight: $disappearHeight, color: $color, transparency: $transparency, xPosition: $xPosition, yPosition: $yPosition}'; 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /lib/bin/dragablegridviewbin.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class DragAbleGridViewBin{ 4 | double dragPointX=0.0; 5 | double dragPointY=0.0; 6 | double lastTimePositionX=0.0; 7 | double lastTimePositionY=0.0; 8 | GlobalKey containerKey=new GlobalKey(); 9 | GlobalKey containerKeyChild=new GlobalKey(); 10 | bool isLongPress=false; 11 | bool dragAble=false; 12 | ///是否隐藏,默认不隐藏 13 | bool offstage=false; 14 | } -------------------------------------------------------------------------------- /lib/bin/gridviewitembin.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutterapp/bin/dragablegridviewbin.dart'; 2 | //import 'package:dragablegridview_flutter/dragablegridviewbin.dart'; 3 | 4 | class ItemBin extends DragAbleGridViewBin{ 5 | 6 | ItemBin( this.data); 7 | 8 | String data; 9 | 10 | @override 11 | String toString() { 12 | return 'ItemBin{data: $data, dragPointX: $dragPointX, dragPointY: $dragPointY, lastTimePositionX: $lastTimePositionX, lastTimePositionY: $lastTimePositionY, containerKey: $containerKey, containerKeyChild: $containerKeyChild, isLongPress: $isLongPress, dragAble: $dragAble}'; 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /lib/components/adsorptionview/adsorptionview.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutterapp/bin/adsorptiondatabin.dart'; 3 | import 'package:flutterapp/components/adsorptionview/equalHeightState.dart'; 4 | import 'package:flutterapp/components/adsorptionview/notEqualHeightState.dart'; 5 | 6 | typedef Widget GetHearWidget (M bin); 7 | typedef Widget GetGeneralItem (M bin); 8 | 9 | class AdsorptionView extends StatefulWidget{ 10 | 11 | final List adsorptionDatas; 12 | final GetHearWidget headChild; 13 | final GetGeneralItem generalItemChild; 14 | final double itemHeight; 15 | final double itemWidth; 16 | final double cacheExtent; 17 | //final bool isEqualHeightItem; 18 | 19 | AdsorptionView({ 20 | @required this.adsorptionDatas, 21 | @required this.headChild, 22 | @required this.generalItemChild, 23 | this.itemHeight:50.0, 24 | this.itemWidth:double.infinity, 25 | this.cacheExtent:30.0, 26 | //@required this.isEqualHeightItem, 27 | }): assert( 28 | adsorptionDatas!=null, 29 | generalItemChild!=null&& 30 | headChild!=null, 31 | ); 32 | 33 | @override 34 | State createState() { 35 | // if(isEqualHeightItem) { 36 | return new AdsorptionViewState(); 37 | // }else{ 38 | // return new AdsorptionViewNotEqualHeightState(); 39 | // } 40 | } 41 | } 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /lib/components/adsorptionview/equalHeightState.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | import 'package:flutterapp/bin/adsorptiondatabin.dart'; 4 | import 'package:flutterapp/components/adsorptionview/adsorptionview.dart'; 5 | 6 | ///此控件适用于固定高度的ListView 7 | class AdsorptionViewState extends State>{ 8 | 9 | ScrollController scrollController=new ScrollController(); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return new Stack( 14 | children: [ 15 | new AdsorptionListView( 16 | scrollController: scrollController, 17 | adsorptionDatas: widget.adsorptionDatas, 18 | generalItemChild: widget.generalItemChild, 19 | headChild: widget.headChild, 20 | itemHeight: widget.itemHeight, 21 | itemWidth: widget.itemWidth, 22 | cacheExtent: widget.cacheExtent, 23 | ), 24 | new GestureDetector( 25 | onTap: (){ 26 | double pixels= scrollController.position.pixels; 27 | int a=pixels~/widget.itemHeight; 28 | for(int i=a;i>=0;i--){ 29 | if(widget.adsorptionDatas[i].isHeader) { 30 | scrollController.animateTo(i*widget.itemHeight, duration: new Duration(milliseconds: 200), curve: Curves.linear); 31 | break; 32 | } 33 | } 34 | }, 35 | child: new HeaderView( 36 | scrollController: scrollController, 37 | headChild: widget.headChild, 38 | adsorptionDatas: widget.adsorptionDatas, 39 | itemWidth: widget.itemWidth, 40 | itemHeight: widget.itemHeight, 41 | ), 42 | ), 43 | ], 44 | ); 45 | } 46 | } 47 | 48 | 49 | class AdsorptionListView extends StatefulWidget{ 50 | final ScrollController scrollController; 51 | final double itemHeight; 52 | final double itemWidth; 53 | final double cacheExtent; 54 | final List adsorptionDatas; 55 | final GetHearWidget headChild; 56 | final GetGeneralItem generalItemChild; 57 | 58 | AdsorptionListView({ 59 | @required this.adsorptionDatas, 60 | @required this.headChild, 61 | @required this.generalItemChild, 62 | this.itemHeight:50.0, 63 | this.itemWidth:double.infinity, 64 | this.cacheExtent:30.0, 65 | @required this.scrollController, 66 | }): assert( 67 | adsorptionDatas!=null, 68 | generalItemChild!=null&& 69 | headChild!=null, 70 | ); 71 | 72 | @override 73 | State createState() { 74 | return AdsorptionListViewState(); 75 | } 76 | } 77 | 78 | class AdsorptionListViewState extends State>{ 79 | 80 | ScrollPhysics scrollPhysics=new ClampingScrollPhysics(); 81 | 82 | @override 83 | Widget build(BuildContext context) { 84 | return new ListView.builder( 85 | physics: scrollPhysics, 86 | cacheExtent: widget.cacheExtent, 87 | controller: widget.scrollController, 88 | itemCount: widget.adsorptionDatas.length, 89 | itemBuilder: (context, index) { 90 | if(widget.adsorptionDatas[index].isHeader){ 91 | return new Container( 92 | width: widget.itemWidth, 93 | height: widget.itemHeight, 94 | child: widget.headChild(widget.adsorptionDatas[index]), 95 | ); 96 | }else{ 97 | return new Container( 98 | width: widget.itemWidth, 99 | height: widget.itemHeight, 100 | child: widget.generalItemChild(widget.adsorptionDatas[index]), 101 | ); 102 | } 103 | }, 104 | ); 105 | } 106 | } 107 | 108 | 109 | class HeaderView extends StatefulWidget{ 110 | 111 | final ScrollController scrollController; 112 | final double itemHeight; 113 | final double itemWidth; 114 | final GetHearWidget headChild; 115 | final List adsorptionDatas; 116 | 117 | HeaderView({ 118 | @required this.scrollController, 119 | this.itemHeight:50.0, 120 | this.itemWidth:double.infinity, 121 | @required this.headChild, 122 | @required this.adsorptionDatas, 123 | }); 124 | 125 | @override 126 | State createState() { 127 | return new HeaderViewState(); 128 | } 129 | } 130 | 131 | class HeaderViewState extends State>{ 132 | double headerOffset=0.0; 133 | T headerStr; 134 | double beforeScroll=0.0; 135 | 136 | @override 137 | void initState() { 138 | headerStr=widget.adsorptionDatas.first; 139 | 140 | widget.scrollController.addListener((){ 141 | //计算滑动了多少距离了 142 | double pixels=widget.scrollController.position.pixels; 143 | 144 | //根据滑动的距离 计算当前可见的第一个Item的Position 145 | int a=pixels~/widget.itemHeight; 146 | //计算滑动出屏幕多少距离 147 | double b=pixels%widget.itemHeight; 148 | double currentScrollPosition=widget.scrollController.position.extentBefore; 149 | //如果下一个item是Header 则偏移 如果不是 则偏移量=0 150 | if(widget.adsorptionDatas[a+1].isHeader){ 151 | setState(() { 152 | // 改变布局 153 | if(currentScrollPosition-beforeScroll<0){ 154 | //检测到再向上划就越出当前组 提前改变header的内容并偏移 155 | for(int i=a;i>=0;i--){ 156 | if(widget.adsorptionDatas[i].isHeader){ 157 | headerStr=widget.adsorptionDatas[i]; 158 | break; 159 | } 160 | } 161 | } 162 | beforeScroll=currentScrollPosition; 163 | headerOffset=-b; 164 | }); 165 | }else{ 166 | //始终使header处于完整显示状态 167 | for(int i=a;i>=0;i--){ 168 | if(widget.adsorptionDatas[i].isHeader) { 169 | if(headerStr != widget.adsorptionDatas[i]) { 170 | setState(() { 171 | headerStr = widget.adsorptionDatas[i]; 172 | }); 173 | } 174 | break; 175 | } 176 | } 177 | if(headerOffset!=0){ 178 | setState(() { 179 | headerOffset=0.0; 180 | }); 181 | } 182 | } 183 | }); 184 | super.initState(); 185 | } 186 | 187 | @override 188 | Widget build(BuildContext context) { 189 | return new Container( 190 | transform: Matrix4.translationValues(0.0, headerOffset, 0.0), 191 | width: widget.itemWidth, 192 | height: widget.itemHeight, 193 | child: widget.headChild(headerStr), 194 | ); 195 | } 196 | } -------------------------------------------------------------------------------- /lib/components/drawablestarttext.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class DrawableStartText extends StatefulWidget{ 4 | 5 | final TextStyle textStyle; 6 | final String text; 7 | final String assetImage; 8 | final int maxLines; 9 | ///尽可能写出图片后大概有多少字母,这样能快速计算第一行显示多少个字母(例如有20个字母 可以设置该值为12左右),默认为0 10 | //final int lettersCountOfAfterImage; 11 | 12 | DrawableStartText({ 13 | this.textStyle, 14 | @required this.text, 15 | @required this.assetImage, 16 | this.maxLines, 17 | //this.lettersCountOfAfterImage:0, 18 | }); 19 | 20 | @override 21 | State createState() { 22 | return new DrawableStartTextState(); 23 | } 24 | } 25 | 26 | 27 | 28 | class DrawableStartTextState extends State{ 29 | 30 | double _textHeight; 31 | GlobalKey rowKey=GlobalKey(); 32 | GlobalKey imageKey=GlobalKey(); 33 | String _topText=""; 34 | String _bottomText=""; 35 | Image _image; 36 | 37 | @override 38 | void initState() { 39 | super.initState(); 40 | //计算文字的高度,根据文字的高度设定图片的高度,然后让图片自适应 41 | TextPainter painter=new TextPainter(); 42 | if(widget.textStyle!=null) { 43 | painter.text = TextSpan(style: widget.textStyle, text: widget.text); 44 | }else{ 45 | painter.text = TextSpan(text: widget.text); 46 | } 47 | painter.maxLines=1; 48 | painter.textDirection=TextDirection.ltr; 49 | painter.layout(); 50 | _textHeight=painter.size.height; 51 | 52 | //在第一帧后计算下第一行能显示多少个字母,然后将字母分成两段显示 53 | WidgetsBinding.instance.addPostFrameCallback((callback){ 54 | _image.image.resolve(new ImageConfiguration()) 55 | .addListener((imageInfo,synchronousCall){ 56 | //计算图片的宽高 57 | double imgHeight = imageInfo.image.height . toDouble(); 58 | double imgWidth = imageInfo.image.width . toDouble(); 59 | //由于图片缩放了。所以根据缩放大小计算出宽图,这里没有用key去取值,是因为取出的值是空的 60 | double scale=_textHeight/imgHeight; 61 | double _imageWidth=imgWidth*scale; 62 | 63 | //再用父控件的宽度减去图片的宽度就是文字显示的宽度 64 | double parentWidth = rowKey.currentContext.findRenderObject().paintBounds.size.width; 65 | double textWidth = parentWidth - _imageWidth; 66 | 67 | int index=0; 68 | //计算出在哪个字母时超出了显示范围 69 | for(;indextextWidth){ 77 | break; 78 | } 79 | } 80 | 81 | //将超出的哪个位置减掉,剩下的字母就不会超出范围了 82 | int validIndex=index-1; 83 | //根据计算的位置,分别截取前半部分 和后半部分显示 84 | setState(() { 85 | _topText= widget.text.substring(0,validIndex); 86 | _bottomText = widget.text.substring(validIndex); 87 | }); 88 | }); 89 | 90 | }); 91 | } 92 | 93 | @override 94 | Widget build(BuildContext context) { 95 | final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context); 96 | return new Column( 97 | crossAxisAlignment: CrossAxisAlignment.start, 98 | children: [ 99 | new Row( 100 | key: rowKey, 101 | children: [ 102 | _image=new Image.asset( 103 | widget.assetImage, 104 | key:imageKey, 105 | height:_textHeight, 106 | fit : BoxFit.fitHeight, 107 | ), 108 | new Text( 109 | _topText, 110 | style: widget.textStyle, 111 | maxLines: 1, 112 | ), 113 | ], 114 | ), 115 | new Text( 116 | _bottomText, 117 | style: widget.textStyle, 118 | textAlign: TextAlign.left, 119 | maxLines: widget.maxLines ==null ? defaultTextStyle.maxLines : widget.maxLines-1, 120 | overflow: widget.maxLines ==null ? defaultTextStyle.overflow : TextOverflow.ellipsis, 121 | ), 122 | ], 123 | ); 124 | } 125 | } -------------------------------------------------------------------------------- /lib/components/marquee.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | 6 | class MarqueeWidget extends StatefulWidget{ 7 | 8 | final String text; 9 | final TextStyle textStyle; 10 | ///滚动方向,水平或者垂直 11 | final Axis scrollAxis; 12 | ///空白部分占控件的百分比 13 | final double ratioOfBlankToScreen; 14 | 15 | MarqueeWidget({ 16 | @required this.text, 17 | this.textStyle, 18 | this.scrollAxis:Axis.horizontal, 19 | this.ratioOfBlankToScreen:0.25, 20 | }) :assert(text!=null,); 21 | 22 | @override 23 | State createState() { 24 | return new MarqueeWidgetState(); 25 | } 26 | } 27 | 28 | class MarqueeWidgetState extends State with SingleTickerProviderStateMixin{ 29 | 30 | ScrollController scroController; 31 | double blankWidth; 32 | double blankHeight; 33 | double position=0.0; 34 | Timer timer; 35 | final double _moveDistance=3.0; 36 | final int _timerRest=100; 37 | GlobalKey _key=GlobalKey(); 38 | 39 | 40 | @override 41 | void initState() { 42 | super.initState(); 43 | scroController=new ScrollController(); 44 | WidgetsBinding.instance.addPostFrameCallback((callback){ 45 | startTimer(); 46 | }); 47 | } 48 | 49 | void startTimer(){ 50 | double widgetWidth = _key.currentContext.findRenderObject().paintBounds.size.width; 51 | double widgetHeight = _key.currentContext.findRenderObject().paintBounds.size.height; 52 | 53 | timer=Timer.periodic(new Duration(milliseconds: _timerRest), (timer){ 54 | double maxScrollExtent=scroController.position.maxScrollExtent; 55 | double pixels=scroController.position.pixels; 56 | //当animateTo的距离大于最大滑动距离时,则要返回第一个child的特定位置,让末尾正好处于最右侧,然后继续滚动,造成跑马灯的假象 57 | if(pixels+_moveDistance>=maxScrollExtent){ 58 | if(widget.scrollAxis==Axis.horizontal){ 59 | //TODO maxScrollExtent是可滑动的最大距离,不可滑动的距离并不计算在内(即ListView的控件宽度),maxScrollExtent + widgetWidth才是children的真正高度 60 | //(maxScrollExtent+widgetWidth-blankWidth)/2 可计算出一个TextView控件的长度,然后再减去widgetWidth。计算出第一个child偏移到最右侧所需要的偏移量 61 | //当animateTo滑动到末尾,但是距末尾还有一段距离,jumpTo的时候要将这段距离考虑进去 pixels-maxScrollExtent 62 | //原始计算公式 (maxScrollExtent+widgetWidth-blankWidth)/2 -widgetWidth + pixels- maxScrollExtent,下面的计算公式是经过简化的 63 | position=(maxScrollExtent-blankWidth-widgetWidth)/2+pixels-maxScrollExtent; 64 | }else{ 65 | position=(maxScrollExtent-blankHeight-widgetHeight)/2+pixels-maxScrollExtent; 66 | } 67 | scroController.jumpTo(position); 68 | } 69 | position+=_moveDistance; 70 | scroController.animateTo(position,duration: new Duration(milliseconds: _timerRest),curve: Curves.linear); 71 | }); 72 | } 73 | 74 | 75 | @override 76 | void didChangeDependencies() { 77 | super.didChangeDependencies(); 78 | double screenWidth=MediaQuery.of(context).size.width; 79 | double screenHeight=MediaQuery.of(context).size.height; 80 | blankWidth=screenWidth*widget.ratioOfBlankToScreen; 81 | blankHeight=screenHeight*widget.ratioOfBlankToScreen; 82 | } 83 | 84 | Widget getBothEndsChild(){ 85 | if(widget.scrollAxis ==Axis.vertical){ 86 | String newString=widget.text.split("").join("\n"); 87 | return new Center( 88 | child: new Text(newString,style: widget.textStyle,textAlign: TextAlign.center,), 89 | ); 90 | } 91 | return new Center( 92 | child:new Text(widget.text,style: widget.textStyle,) 93 | ); 94 | } 95 | 96 | Widget getCenterChild(){ 97 | if(widget.scrollAxis ==Axis.horizontal){ 98 | return new Container(width: blankWidth); 99 | }else{ 100 | return new Container(height: blankHeight); 101 | } 102 | } 103 | 104 | 105 | 106 | @override 107 | void dispose() { 108 | super.dispose(); 109 | timer.cancel(); 110 | } 111 | 112 | @override 113 | Widget build(BuildContext context) { 114 | return new ListView( 115 | key: _key, 116 | scrollDirection: widget.scrollAxis, 117 | controller: scroController, 118 | physics: new NeverScrollableScrollPhysics(), 119 | children: [ 120 | getBothEndsChild(), 121 | getCenterChild(), 122 | getBothEndsChild(), 123 | ], 124 | ); 125 | } 126 | } -------------------------------------------------------------------------------- /lib/components/radarchart.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'dart:math'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class RadarChart extends StatefulWidget{ 6 | 7 | final double radarChartRadius=200.0; 8 | final Map maps={"Java":20,"Flutter":20,"Object-C":20,"C#":20,"Kotlin":20}; 9 | 10 | @override 11 | State createState() { 12 | return RadarChartState() ; 13 | } 14 | 15 | } 16 | 17 | class RadarChartState extends State{ 18 | 19 | PictureRecorder _recorder; 20 | Picture picture; 21 | double maxWidth=0.0; 22 | double maxHeight=0.0; 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return new Container( 27 | child: new CustomPaint( 28 | size: new Size(maxWidth,maxHeight), 29 | painter: new RadarChartPainter( 30 | radarChartRadius:widget.radarChartRadius, 31 | picture: picture, 32 | ), 33 | ), 34 | ); 35 | } 36 | 37 | 38 | @override 39 | void initState() { 40 | super.initState(); 41 | _recorder=new PictureRecorder(); 42 | drawRadarChartBackground(); 43 | } 44 | 45 | 46 | ///画雷达图的底图 47 | void drawRadarChartBackground(){ 48 | Canvas canvas=Canvas(_recorder); 49 | //计算每边占多少角度 50 | double angle=pi*2/widget.maps.length; 51 | Paint linePaint=Paint(); 52 | //文字和雷达图的间距 53 | double spaceBetweenChartAndText=5.0; 54 | 55 | //计算雷达图中最长的文字占用的宽度,在切大小的时候必须考虑到文字占用的大小 56 | TextPainter painterSmall=new TextPainter(); 57 | painterSmall.textDirection=TextDirection.ltr; 58 | double maxTextWidth=0.0; 59 | widget.maps.keys.forEach((key){ 60 | painterSmall.text=new TextSpan(text: key,style: new TextStyle(fontSize: 15.0,color: Colors.black)); 61 | painterSmall.layout(); 62 | if(painterSmall.size.width>maxTextWidth){ 63 | maxTextWidth=painterSmall.size.width; 64 | } 65 | }); 66 | //在半径的基础上加上最大文字宽度,由于是两边有文字,所以*2 ,加10是因为文字和图之间有空隙 67 | maxWidth=widget.radarChartRadius+maxTextWidth*2+spaceBetweenChartAndText*2; 68 | maxHeight=widget.radarChartRadius+painterSmall.size.height*2+spaceBetweenChartAndText*2; 69 | canvas.clipRect(new Rect.fromLTRB(0.0, 0.0, maxWidth, maxHeight)); 70 | 71 | 72 | canvas.translate(maxWidth/2, maxHeight/2); 73 | //旋转单边一半所需要的角度 74 | double halfAngle=angle/2; 75 | //绘制横线的半径 76 | double linesRadius; 77 | //横线与横线之间的间隙 78 | double linesSpace=cos(halfAngle)*widget.radarChartRadius/10; 79 | 80 | //绘制网格图 81 | canvas.save(); 82 | //canvas.rotate(-pi/2-halfAngle); 83 | //为什么角度是反的? Why is the angle reversed? 84 | canvas.rotate(pi/2+halfAngle); 85 | for(int i=0;i=(1.875*pi) || accumulateAngle=pi/8 && accumulateAngle=pi*0.375 && accumulateAngle=pi*0.625 && accumulateAngle=pi*0.875 && accumulateAngle=pi*1.125 && accumulateAngle=pi*1.375 && accumulateAngle createState() { 37 | return new WaveProgressBarState(); 38 | } 39 | } 40 | 41 | 42 | class WaveProgressBarState extends State with SingleTickerProviderStateMixin{ 43 | double _moveForwardDark=0.0; 44 | double _moveForwardLight=0.0; 45 | double _waterHeight; 46 | double _percentage; 47 | Animation animation; 48 | AnimationController animationController; 49 | VoidCallback _voidCallback; 50 | Random _random=new Random(); 51 | Picture _lightWavePic; 52 | Picture _darkWavePic; 53 | 54 | @override 55 | void dispose() { 56 | animationController.stop(); 57 | animationController.dispose(); 58 | super.dispose(); 59 | } 60 | 61 | @override 62 | void initState() { 63 | super.initState(); 64 | _percentage=widget.percentage; 65 | _waterHeight=(1-_percentage) * widget.size.height; 66 | widget.progressController.bezierCurveState=this; 67 | WavePictureGenerator generator=WavePictureGenerator(widget.size,widget.waveDistance,widget.waveHeight,widget.waterColor); 68 | _lightWavePic=generator.drawLightWave(); 69 | _darkWavePic=generator.drawDarkWave(); 70 | animationController = new AnimationController(duration: const Duration(milliseconds: 3000), vsync: this); 71 | 72 | WidgetsBinding widgetsBinding=WidgetsBinding.instance; 73 | widgetsBinding.addPostFrameCallback((callback){ 74 | widgetsBinding.addPersistentFrameCallback((callback) { 75 | if(mounted){ 76 | setState(() { 77 | _moveForwardDark= _moveForwardDark - widget.flowSpeed- _random.nextDouble()-1; 78 | if(_moveForwardDark<= - widget.waveDistance*4){ 79 | _moveForwardDark=_moveForwardDark+widget.waveDistance*4; 80 | } 81 | 82 | _moveForwardLight = _moveForwardLight- widget.flowSpeed- _random.nextDouble(); 83 | if(_moveForwardLight <= - widget.waveDistance*4){ 84 | _moveForwardLight = _moveForwardLight+widget.waveDistance*4; 85 | } 86 | }); 87 | widgetsBinding.scheduleFrame(); 88 | } 89 | }); 90 | }); 91 | } 92 | 93 | 94 | 95 | @override 96 | Widget build(BuildContext context) { 97 | return new CustomPaint( 98 | size: widget.size, 99 | painter: new BezierCurvePainter( 100 | moveForward:_moveForwardDark, 101 | textStyle:widget.textStyle , 102 | circleStrokeWidth: widget.circleStrokeWidth, 103 | strokeCircleColor: widget.strokeCircleColor, 104 | percentage: _percentage , 105 | moveForwardLight: _moveForwardLight, 106 | lightWavePic: _lightWavePic, 107 | darkWavePic: _darkWavePic, 108 | waterHeight: _waterHeight, 109 | ), 110 | ); 111 | } 112 | 113 | void changeWaterHeight(double h){ 114 | initAnimation(_percentage ,h); 115 | animationController.forward(); 116 | } 117 | 118 | 119 | void initAnimation(double old ,double newPercentage){ 120 | animation = new Tween(begin: old, end: newPercentage).animate(animationController); 121 | 122 | animation.addListener(_voidCallback=() { 123 | setState(() { 124 | double value = animation.value; 125 | _percentage=value; 126 | double newHeight=(1-_percentage) * widget.size.height; 127 | _waterHeight=newHeight; 128 | }); 129 | }); 130 | 131 | animation.addStatusListener((animationStatus){ 132 | if(animationStatus==AnimationStatus.completed){ 133 | animation.removeListener(_voidCallback); 134 | animationController.reset(); 135 | }else if(animationStatus==AnimationStatus.forward){ 136 | 137 | } 138 | }); 139 | } 140 | } 141 | 142 | 143 | class WavePictureGenerator{ 144 | PictureRecorder _recorder; 145 | final Size size; 146 | final double _waveDistance; 147 | final double _waveHeight; 148 | final Color _waterColor; 149 | int _maxCount; 150 | double waterHeight; 151 | double _targetWidth; 152 | double _targetHeight; 153 | 154 | WavePictureGenerator(this.size,this._waveDistance,this._waveHeight,this._waterColor){ 155 | double oneDistance=_waveDistance*4; 156 | int count=(size.width/oneDistance).ceil()+1; 157 | _targetWidth=count*oneDistance; 158 | _maxCount=count*4+1; 159 | waterHeight=size.height/2; 160 | _targetHeight=size.height+waterHeight; 161 | } 162 | 163 | Picture drawDarkWave(){ 164 | return drawWaves(true); 165 | } 166 | 167 | Picture drawLightWave(){ 168 | return drawWaves(false); 169 | } 170 | 171 | Picture drawWaves(bool isDarkWave){ 172 | _recorder=PictureRecorder(); 173 | Canvas canvas=Canvas(_recorder); 174 | canvas.clipRect(new Rect.fromLTWH(0.0, 0.0, _targetWidth, _targetHeight)); 175 | Paint paint =Paint(); 176 | if(isDarkWave){ 177 | paint.color = _waterColor; 178 | }else{ 179 | paint.color = _waterColor.withAlpha(0x88); 180 | } 181 | paint.style=PaintingStyle.fill; 182 | canvas.drawPath(createBezierPath(isDarkWave), paint); 183 | return _recorder.endRecording(); 184 | } 185 | 186 | 187 | Path createBezierPath(bool isDarkWave){ 188 | 189 | Path path=Path(); 190 | path.moveTo(0, waterHeight); 191 | 192 | double lastPoint=0.0; 193 | int m=0; 194 | double waves; 195 | for(int i=0;i<_maxCount-2;i=i+2){ 196 | if(m%2==0){ 197 | waves=waterHeight+_waveHeight; 198 | }else{ 199 | waves=waterHeight-_waveHeight; 200 | } 201 | path.cubicTo(lastPoint, waterHeight, lastPoint+_waveDistance, waves, lastPoint+_waveDistance*2, waterHeight); 202 | lastPoint=lastPoint+_waveDistance*2; 203 | m++; 204 | } 205 | if(isDarkWave){ 206 | path.lineTo(lastPoint,_targetHeight); 207 | path.lineTo(0,_targetHeight); 208 | }else{ 209 | double waveHeightMax=waterHeight+_waveHeight+10.0; 210 | path.lineTo(lastPoint, waveHeightMax); 211 | path.lineTo(0,waveHeightMax); 212 | } 213 | path.close(); 214 | return path; 215 | } 216 | } 217 | 218 | 219 | class BezierCurvePainter extends CustomPainter{ 220 | 221 | final double moveForward; 222 | final Color strokeCircleColor; 223 | final TextStyle textStyle; 224 | final double circleStrokeWidth; 225 | final double percentage; 226 | final double moveForwardLight; 227 | final Picture darkWavePic; 228 | final Picture lightWavePic; 229 | final double waterHeight; 230 | final Paint _paints =Paint(); 231 | 232 | BezierCurvePainter({ 233 | @required this.moveForward, 234 | @required this.strokeCircleColor, 235 | @required this.textStyle, 236 | @required this.circleStrokeWidth, 237 | @required this.percentage, 238 | @required this.moveForwardLight, 239 | @required this.darkWavePic, 240 | @required this.lightWavePic, 241 | @required this.waterHeight, 242 | }); 243 | 244 | @override 245 | void paint(Canvas canvas, Size size) { 246 | Paint layerPaint=new Paint(); 247 | 248 | double halfSizeHeight = size.height/2; 249 | double halfSizeWidth = size.width/2; 250 | double radius=min(size.width, size.height)/2-circleStrokeWidth/2; 251 | 252 | //由于在绘制图片的时候在波浪上面有50%高度的空白部分,所以在这里必须减掉 253 | double targetHeight=waterHeight-halfSizeHeight; 254 | 255 | canvas.saveLayer(new Rect.fromLTRB(0.0, 0.0, size.width, size.height), layerPaint); 256 | //绘制淡颜色的海浪 257 | canvas.save(); 258 | canvas.translate(moveForwardLight, targetHeight); 259 | canvas.drawPicture(lightWavePic); 260 | 261 | //绘制深颜色的海浪 262 | double moveDistance=moveForward-moveForwardLight; 263 | canvas.translate(moveDistance, 0.0); 264 | canvas.drawPicture(darkWavePic); 265 | canvas.restore(); 266 | 267 | layerPaint.blendMode=BlendMode.dstIn ; 268 | canvas.saveLayer(new Rect.fromLTRB(0.0, 0.0, size.width, size.height), layerPaint); 269 | 270 | canvas.drawCircle(new Offset(halfSizeWidth, halfSizeHeight), radius-circleStrokeWidth, _paints); 271 | canvas.restore(); 272 | canvas.restore(); 273 | 274 | 275 | _paints.style=PaintingStyle.stroke; 276 | _paints.color=strokeCircleColor; 277 | _paints.strokeWidth=circleStrokeWidth; 278 | canvas.drawCircle(new Offset(halfSizeWidth, halfSizeHeight), radius, _paints); 279 | 280 | TextPainter textPainter = new TextPainter(); 281 | textPainter.textDirection = TextDirection.ltr; 282 | textPainter.text = new TextSpan(text: (percentage*100).toInt().toString()+"%", 283 | style: textStyle,); 284 | textPainter.layout(); 285 | double textStarPositionX = halfSizeWidth-textPainter.size.width/2; 286 | double textStarPositionY = halfSizeHeight-textPainter.size.height/2; 287 | textPainter.paint(canvas, new Offset(textStarPositionX, textStarPositionY)); 288 | } 289 | 290 | @override 291 | bool shouldRepaint(CustomPainter oldDelegate) { 292 | return true; 293 | } 294 | } 295 | 296 | class WaterController { 297 | WaveProgressBarState bezierCurveState; 298 | 299 | void changeProgressRate(double h){ 300 | if(bezierCurveState!=null) { 301 | bezierCurveState.changeWaterHeight(h); 302 | } 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:camera/camera.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutterapp/Home.dart'; 4 | 5 | 6 | List cameras; 7 | 8 | Future main() async { 9 | 10 | // Fetch the available cameras before initializing the app. 11 | try { 12 | cameras = await availableCameras(); 13 | } on CameraException catch (e) { 14 | logError(e.code, e.description); 15 | } 16 | runApp( 17 | new MaterialApp( 18 | title: 'Startup Name Generator', 19 | showPerformanceOverlay: false, 20 | theme: new ThemeData( 21 | primaryColor: Colors.white, 22 | ), 23 | home: new MyHomePage(), 24 | ) 25 | ); 26 | } 27 | 28 | 29 | void logError(String code, String message) => 30 | print('Error: $code\nError Message: $message'); -------------------------------------------------------------------------------- /lib/ui/firstpage/adsorptionviewdemo.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | import 'package:flutterapp/bin/adsorptionlistbin.dart'; 4 | import 'package:flutterapp/components/adsorptionview/adsorptionview.dart'; 5 | 6 | class AdsorptionViewDemo extends StatefulWidget{ 7 | @override 8 | State createState() { 9 | List adsorptionDatas=new List(); 10 | AdsorptionListBin adsorptionData; 11 | 12 | adsorptionData=new AdsorptionListBin("A"); 13 | adsorptionData.isHeader=true; 14 | adsorptionDatas.add(adsorptionData); 15 | 16 | adsorptionData=new AdsorptionListBin("阿杜"); 17 | adsorptionDatas.add(adsorptionData); 18 | 19 | adsorptionData=new AdsorptionListBin("阿宝"); 20 | adsorptionDatas.add(adsorptionData); 21 | 22 | adsorptionData=new AdsorptionListBin("艾夫杰尼"); 23 | adsorptionDatas.add(adsorptionData); 24 | 25 | adsorptionData=new AdsorptionListBin("阿牛"); 26 | adsorptionDatas.add(adsorptionData); 27 | 28 | adsorptionData=new AdsorptionListBin("安苏羽"); 29 | adsorptionDatas.add(adsorptionData); 30 | 31 | adsorptionData=new AdsorptionListBin("阿勒长青"); 32 | adsorptionDatas.add(adsorptionData); 33 | 34 | 35 | adsorptionData=new AdsorptionListBin("B"); 36 | adsorptionData.isHeader=true; 37 | adsorptionDatas.add(adsorptionData); 38 | 39 | adsorptionData=new AdsorptionListBin("白小白"); 40 | adsorptionDatas.add(adsorptionData); 41 | 42 | adsorptionData=new AdsorptionListBin("白羽毛"); 43 | adsorptionDatas.add(adsorptionData); 44 | 45 | adsorptionData=new AdsorptionListBin("Bridge"); 46 | adsorptionDatas.add(adsorptionData); 47 | 48 | adsorptionData=new AdsorptionListBin("斑马"); 49 | adsorptionDatas.add(adsorptionData); 50 | 51 | adsorptionData=new AdsorptionListBin("白一阳"); 52 | adsorptionDatas.add(adsorptionData); 53 | 54 | adsorptionData=new AdsorptionListBin("白举纲"); 55 | adsorptionDatas.add(adsorptionData); 56 | 57 | adsorptionData=new AdsorptionListBin("暴林"); 58 | adsorptionDatas.add(adsorptionData); 59 | 60 | 61 | adsorptionData=new AdsorptionListBin("C"); 62 | adsorptionData.isHeader=true; 63 | adsorptionDatas.add(adsorptionData); 64 | 65 | adsorptionData=new AdsorptionListBin("陈奕迅"); 66 | adsorptionDatas.add(adsorptionData); 67 | 68 | adsorptionData=new AdsorptionListBin("陈小春"); 69 | adsorptionDatas.add(adsorptionData); 70 | 71 | adsorptionData=new AdsorptionListBin("成龙"); 72 | adsorptionDatas.add(adsorptionData); 73 | 74 | adsorptionData=new AdsorptionListBin("陈百强"); 75 | adsorptionDatas.add(adsorptionData); 76 | 77 | adsorptionData=new AdsorptionListBin("迟志强"); 78 | adsorptionDatas.add(adsorptionData); 79 | 80 | adsorptionData=new AdsorptionListBin("崔健"); 81 | adsorptionDatas.add(adsorptionData); 82 | 83 | adsorptionData=new AdsorptionListBin("陈晓东"); 84 | adsorptionDatas.add(adsorptionData); 85 | 86 | adsorptionData=new AdsorptionListBin("陈学冬"); 87 | adsorptionDatas.add(adsorptionData); 88 | 89 | adsorptionData=new AdsorptionListBin("蔡国庆"); 90 | adsorptionDatas.add(adsorptionData); 91 | 92 | adsorptionData=new AdsorptionListBin("陈冠希"); 93 | adsorptionDatas.add(adsorptionData); 94 | 95 | adsorptionData=new AdsorptionListBin("陈琳"); 96 | adsorptionDatas.add(adsorptionData); 97 | return new AdsorptionViewState(adsorptionDatas); 98 | } 99 | } 100 | 101 | ///此控件适用于固定高度的ListView 102 | class AdsorptionViewState extends State{ 103 | 104 | AdsorptionViewState(this.adsorptionDatas); 105 | 106 | List adsorptionDatas; 107 | double itemHeight=50.0; 108 | 109 | 110 | @override 111 | Widget build(BuildContext context) { 112 | return new Scaffold( 113 | appBar: new AppBar( 114 | title: new Text("吸附布局"), 115 | ), 116 | body:new AdsorptionView( 117 | adsorptionDatas: adsorptionDatas, 118 | generalItemChild: (AdsorptionListBin bin) { 119 | print("build Item Child ${bin.headerName}"); 120 | return new Container( 121 | height: 60, 122 | alignment: Alignment.centerLeft, 123 | margin: EdgeInsets.fromLTRB(15.0, 0.0, 0.0, 0.0), 124 | child: new Text( 125 | bin.headerName, 126 | style: new TextStyle(fontSize: 18.0, color: Colors.grey), 127 | ), 128 | ); 129 | }, 130 | headChild: (AdsorptionListBin bin) { 131 | print("build head Child"); 132 | return new Container( 133 | height: 50, 134 | color: Colors.grey, 135 | alignment: Alignment.centerLeft, 136 | padding: EdgeInsets.fromLTRB(15.0, 0.0, 0.0, 0.0), 137 | child: new Text( 138 | bin.headerName, 139 | style: new TextStyle( fontSize: 20.0,color: Colors.black), 140 | ), 141 | ); 142 | }, 143 | ), 144 | ); 145 | } 146 | } 147 | 148 | 149 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /lib/ui/firstpage/animations.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/animation.dart'; 5 | 6 | import 'package:flutter/rendering.dart'; 7 | 8 | class AnimationWidget extends StatefulWidget{ 9 | @override 10 | State createState() { 11 | return new AnimationState(); 12 | } 13 | 14 | } 15 | 16 | class AnimationState extends State with SingleTickerProviderStateMixin{ 17 | 18 | final double imageWidth=90.0; 19 | final double imageHeight=160.0; 20 | Animation animation; 21 | AnimationController controller; 22 | double preXPosition; 23 | double screenWidth; 24 | double magins; 25 | double scales=0.0; 26 | List imagesPaths; 27 | bool directionLeft=true; 28 | 29 | @override 30 | void initState() { 31 | controller = new AnimationController(duration: const Duration(milliseconds: 430), vsync: this); 32 | animation = new Tween(begin: 0.0, end: 1.0).animate(controller) 33 | ..addListener(() { 34 | setState(() { 35 | // the state that has changed here is the animation object’s value 36 | if(directionLeft){ 37 | scales=animation.value; 38 | }else{ 39 | scales=1.0-animation.value; 40 | } 41 | print("**********"); 42 | }); 43 | }); 44 | animation.addStatusListener((animationStatus){ 45 | if(animationStatus==AnimationStatus.completed){ 46 | setState(() { 47 | controller.reset(); 48 | if(directionLeft){ 49 | String a=imagesPaths[3]; 50 | imagesPaths.removeLast(); 51 | imagesPaths.insert(0, a); 52 | } 53 | scales=0.0; 54 | }); 55 | 56 | }else if(animationStatus==AnimationStatus.forward){ 57 | setState(() { 58 | if(!directionLeft){ 59 | String a=imagesPaths[0]; 60 | imagesPaths.removeAt(0); 61 | imagesPaths.add(a); 62 | } 63 | }); 64 | } 65 | }); 66 | 67 | super.initState(); 68 | 69 | imagesPaths=new List(); 70 | imagesPaths.add("images/chaonan3.jpeg"); 71 | imagesPaths.add("images/chaonan4.jpeg"); 72 | imagesPaths.add("images/chaonan2.jpg"); 73 | imagesPaths.add("images/chaonan1.jpeg"); 74 | } 75 | 76 | @override 77 | void didChangeDependencies() { 78 | super.didChangeDependencies(); 79 | screenWidth=MediaQuery.of(context).size.width; 80 | magins=(screenWidth-60-imageWidth*2.5)/2.0; 81 | print("magins is $magins"); 82 | } 83 | 84 | 85 | 86 | /* 第一排的magn不用动 宽高不用动 87 | * 中间的View宽高放大 magin加大 88 | * 最后一排 也就是最前面的界面宽高不用动,magin快速变大 89 | */ 90 | Row creatRow(double imgHeight,double imgWidth,double maginSize,String imgPath){ 91 | return new Row( 92 | mainAxisAlignment: MainAxisAlignment.end, 93 | children: [ 94 | new Center( 95 | child: new Container( 96 | width: screenWidth, 97 | alignment: Alignment.centerRight, 98 | child: new OverflowBox( 99 | maxWidth: double.infinity, 100 | maxHeight: double.infinity, 101 | alignment: Alignment.centerRight, 102 | child: new Container( 103 | height: imgHeight, 104 | width: imgWidth, 105 | margin: new EdgeInsets.fromLTRB(0.0, 0.0, maginSize, 0.0), 106 | child: new Image.asset(imgPath), 107 | ), 108 | ), 109 | ), 110 | ), 111 | ], 112 | ); 113 | } 114 | 115 | 116 | 117 | dispose() { 118 | controller.dispose(); 119 | super.dispose(); 120 | } 121 | 122 | @override 123 | Widget build(BuildContext context) { 124 | return new Scaffold( 125 | appBar: new AppBar( 126 | title: new Text("AnimationWidget"), 127 | ), 128 | body: new Center( 129 | child: new GestureDetector( 130 | onHorizontalDragDown: (getureDragDownCallback){ 131 | print("onHorizontalDragDown"); 132 | }, 133 | onHorizontalDragStart: (getureDragStartCallback){ 134 | preXPosition=getureDragStartCallback.globalPosition.dx; 135 | }, 136 | onHorizontalDragUpdate: (getureDragUpdateCallback){ 137 | if((preXPosition-getureDragUpdateCallback.globalPosition.dx)>50&&preXPosition!=0.0){ 138 | print("动画启动"); 139 | preXPosition=0.0; 140 | directionLeft=true; 141 | controller.forward(); 142 | }else if((preXPosition-getureDragUpdateCallback.globalPosition.dx)<-50&&preXPosition!=0.0){ 143 | print("动画启动"); 144 | preXPosition=0.0; 145 | directionLeft=false; 146 | controller.forward(); 147 | } 148 | }, 149 | onHorizontalDragEnd: (getureDragEndCallback){ 150 | preXPosition=0.0; 151 | }, 152 | child: new Stack( 153 | children: [ 154 | creatRow(imageHeight*1.9, imageWidth*1.9, 30.0, imagesPaths[0]), 155 | creatRow(imageHeight*(1.9+0.3*scales), imageWidth*(1.9+0.3*scales), 30.0+scales*magins, imagesPaths[1]), 156 | creatRow(imageHeight*(2.2+0.3*scales), imageWidth*(2.2+0.3*scales),magins+30+scales*magins,imagesPaths[2]), 157 | creatRow(imageHeight*2.5,imageWidth*2.5,magins*2+30+scales*(imageWidth*2.5+40),imagesPaths[3]), 158 | ], 159 | ), 160 | ) 161 | ), 162 | ); 163 | } 164 | } -------------------------------------------------------------------------------- /lib/ui/firstpage/dragablegridviewdemo.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutterapp/bin/gridviewitembin.dart'; 3 | import 'package:flutterapp/components/dragablegridview.dart'; 4 | //import 'package:dragablegridview_flutter/dragablegridview_flutter.dart'; 5 | 6 | 7 | class DragAbleGridViewDemo extends StatefulWidget{ 8 | @override 9 | State createState() { 10 | return new DragAbleGridViewDemoState(); 11 | } 12 | 13 | } 14 | 15 | class DragAbleGridViewDemoState extends State{ 16 | 17 | List itemBins=new List(); 18 | String actionTxtEdit="编辑"; 19 | String actionTxtComplete="完成"; 20 | String actionTxt; 21 | var editSwitchController=EditSwitchController(); 22 | final List heroes=["鲁班","虞姬","甄姬","黄盖","张飞","关羽","刘备","曹操","赵云","孙策","庄周","廉颇","后裔","妲己","荆轲",]; 23 | 24 | @override 25 | void initState() { 26 | super.initState(); 27 | actionTxt=actionTxtEdit; 28 | heroes.forEach((heroName) { 29 | itemBins.add(new ItemBin(heroName)); 30 | } 31 | ); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | return new Scaffold( 37 | appBar: new AppBar( 38 | title: new Text("可拖拽GridView"), 39 | actions: [ 40 | new Center( 41 | child: new GestureDetector( 42 | child: new Container( 43 | child: new Text(actionTxt,style: TextStyle(fontSize: 19.0),), 44 | margin: EdgeInsets.only(right: 12), 45 | ), 46 | onTap: (){ 47 | changeActionState(); 48 | editSwitchController.editStateChanged(); 49 | }, 50 | ) 51 | ) 52 | ], 53 | ), 54 | body: new DragAbleGridView( 55 | mainAxisSpacing:10.0, 56 | crossAxisSpacing:10.0, 57 | childAspectRatio:1.8, 58 | crossAxisCount: 4, 59 | itemBins:itemBins, 60 | editSwitchController:editSwitchController, 61 | /******************************new parameter*********************************/ 62 | isOpenDragAble: true, 63 | animationDuration: 300, //milliseconds 64 | longPressDuration: 800, //milliseconds 65 | /******************************new parameter*********************************/ 66 | deleteIcon: new Image.asset("images/close.png",width: 15.0 ,height: 15.0 ), 67 | child: (int position){ 68 | return new Container( 69 | padding: EdgeInsets.fromLTRB(8.0, 5.0, 8.0, 5.0), 70 | decoration: new BoxDecoration( 71 | borderRadius: BorderRadius.all(new Radius.circular(3.0)), 72 | border: new Border.all(color: Colors.blue), 73 | ), 74 | //因为本布局和删除图标同处于一个Stack内,设置marginTop和marginRight能让图标处于合适的位置 75 | //Because this layout and the delete_Icon are in the same Stack, setting marginTop and marginRight will make the icon in the proper position. 76 | margin: EdgeInsets.only(top: 6.0,right: 6.0), 77 | child: new Text( 78 | itemBins[position].data, 79 | style: new TextStyle(fontSize: 16.0,color: Colors.blue),), 80 | ); 81 | }, 82 | editChangeListener: (){ 83 | changeActionState(); 84 | }, 85 | ), 86 | ); 87 | } 88 | 89 | void changeActionState(){ 90 | if(actionTxt==actionTxtEdit){ 91 | setState(() { 92 | actionTxt=actionTxtComplete; 93 | }); 94 | }else{ 95 | setState(() { 96 | actionTxt=actionTxtEdit; 97 | }); 98 | } 99 | } 100 | } 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /lib/ui/firstpage/firstPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutterapp/ui/firstpage/adsorptionviewdemo.dart'; 3 | import 'package:flutterapp/ui/firstpage/dragablegridviewdemo.dart'; 4 | import 'package:flutterapp/ui/secondpage/loginanimdemo.dart'; 5 | import 'package:flutterapp/ui/firstpage/pulltorefreshdemo.dart'; 6 | import 'package:flutterapp/bin/appinfobin.dart'; 7 | import 'package:flutterapp/ui/firstpage/animations.dart'; 8 | import 'package:flutterapp/ui/firstpage/marqueedemo.dart'; 9 | import 'package:flutterapp/ui/secondpage/timepicker.dart'; 10 | 11 | class FirstPage extends StatefulWidget{ 12 | 13 | @override 14 | State createState() { 15 | return new FirdtPageState(); 16 | } 17 | 18 | } 19 | 20 | class FirdtPageState extends State{ 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return new Scaffold( 25 | appBar: new AppBar( 26 | title: new Text("CustomWidght",style: new TextStyle(fontSize: 18.0),), 27 | actions: [ 28 | new IconButton( 29 | icon: new Icon(Icons.search), 30 | onPressed: null, 31 | iconSize: 29.0, 32 | disabledColor: Colors.white,) 33 | ] 34 | ,), 35 | body: new Container( 36 | // ignore: conflicting_dart_import 37 | child: new ListViewWidgets(), 38 | ) 39 | ); 40 | } 41 | } 42 | 43 | 44 | class ListViewWidgets extends StatefulWidget{ 45 | 46 | var constantList = [ 47 | new AppInfoBin("Animations", "2018-07-09 17:30", "模仿京东品质潮男页面的动画效果",false), 48 | new AppInfoBin("MarqueeWidget", "2018-07-12 15:32", "跑马灯效果",false), 49 | new AppInfoBin("PullAndPush", "2018-07-27 09:45", "上下拉刷新",false), 50 | new AppInfoBin("DragAbleGridView", "2018-08-15 15:30", "可拖拽的GridView",false), 51 | new AppInfoBin("AdsorptionView", "2018-09-06 16:30", "可以吸附顶部的布局",false), 52 | ]; 53 | 54 | @override 55 | State createState() { 56 | return new ListState(); 57 | } 58 | 59 | } 60 | 61 | class ListState extends State{ 62 | 63 | @override 64 | Widget build(BuildContext context) { 65 | return new ListView.builder( 66 | itemCount: widget.constantList.length, 67 | scrollDirection: Axis.vertical, 68 | itemBuilder: (BuildContext context,int index){ 69 | return new GestureDetector( 70 | onTapDown: (TapDownDetails details){ 71 | setState(() { 72 | widget.constantList[index].isTapDown=true; 73 | }); 74 | }, 75 | onTapCancel: (){ 76 | setState(() { 77 | widget.constantList[index].isTapDown=false; 78 | }); 79 | }, 80 | onTapUp:(TapUpDetails details){ 81 | setState(() { 82 | widget.constantList[index].isTapDown=false; 83 | }); 84 | if(index==0){ 85 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) { 86 | return new AnimationWidget(); 87 | })); 88 | }else if(index==1){ 89 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) { 90 | return new MarqueeWidgetDemo(); 91 | })); 92 | }else if(index==2){ 93 | Navigator.of(context).push(new PageRouteBuilder( 94 | pageBuilder: (BuildContext context, Animation animation, Animation secondaryAnimation) { 95 | return new PullAndPushTest(); 96 | }, 97 | transitionsBuilder: (BuildContext context, Animation animation, Animation secondaryAnimation, Widget child) { 98 | return SlideTransition( 99 | position: new Tween( 100 | begin: const Offset(-1.0, 0.0), 101 | end: Offset.zero, 102 | ).animate(animation), 103 | child: child, 104 | ); 105 | }, 106 | )); 107 | }else if(index==3){ 108 | Navigator.of(context).push(new PageRouteBuilder( 109 | pageBuilder: (BuildContext context, Animation animation, Animation secondaryAnimation) { 110 | return new DragAbleGridViewDemo(); 111 | }, 112 | transitionsBuilder: (BuildContext context, Animation animation, Animation secondaryAnimation, Widget child) { 113 | return ScaleTransition( 114 | scale: new Tween( 115 | begin: 0.3, 116 | end: 1.0, 117 | ).animate(animation), 118 | child: child, 119 | ); 120 | }, 121 | )); 122 | }else if(index==4){ 123 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) { 124 | return new AdsorptionViewDemo(); 125 | })); 126 | } 127 | }, 128 | child:new Card( 129 | color: widget.constantList[index].isTapDown?Color(0xFFF4CB28):null, 130 | child: new Container( 131 | padding: new EdgeInsets.all(10.0), 132 | child: new ListTile( 133 | subtitle: new Container( 134 | child: new Column( 135 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 136 | children: [ 137 | new Row( 138 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 139 | children: [ 140 | new Text(widget.constantList[index].title,style: new TextStyle(fontWeight: FontWeight.bold,fontSize: 16.0,color: Colors.black),) 141 | ], 142 | ), 143 | new Row( 144 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 145 | children: [ 146 | new Row( 147 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 148 | crossAxisAlignment: CrossAxisAlignment.center, 149 | children: [ 150 | new Text("时间",style: new TextStyle(fontSize: 13.0)), 151 | new Text(widget.constantList[index].times,style: new TextStyle(fontSize: 13.0)), 152 | ], 153 | ) 154 | ], 155 | ), 156 | new Row( 157 | children: [ 158 | new Expanded( 159 | child:new Container( 160 | padding: const EdgeInsets.fromLTRB(0.0, 8.0, 0.0, 2.0), 161 | child: new Text(widget.constantList[index].content,maxLines: 1,overflow: TextOverflow.ellipsis,), 162 | ), 163 | ) 164 | ], 165 | ) 166 | ], 167 | ), 168 | ), 169 | trailing: new Icon(Icons.keyboard_arrow_right,color: Colors.grey,), 170 | ), 171 | ), 172 | ) 173 | ); 174 | },); 175 | } 176 | 177 | } -------------------------------------------------------------------------------- /lib/ui/firstpage/marqueedemo.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutterapp/components/marquee.dart'; 3 | //import 'package:marquee_flutter/marquee_flutter.dart'; 4 | 5 | 6 | class MarqueeWidgetDemo extends StatelessWidget{ 7 | @override 8 | Widget build(BuildContext context) { 9 | return new Scaffold( 10 | appBar: new AppBar( 11 | title: new Text("跑马灯"), 12 | ), 13 | body:new Container( 14 | color: Colors.blueGrey, 15 | height: 30, 16 | child: new MarqueeWidget( 17 | text: "ListView即滚动列表控件,能将子控件组成可滚动的列表。当你需要排列的子控件超出容器大小", 18 | textStyle: new TextStyle(fontSize: 16.0), 19 | scrollAxis: Axis.horizontal, 20 | ), 21 | ) 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/ui/firstpage/pulltorefreshdemo.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import 'dart:ui'; 4 | 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter/rendering.dart'; 7 | import 'package:flutterapp/components/pulltorefresh.dart'; 8 | //import 'package:pulltorefresh_flutter/pulltorefresh_flutter.dart'; 9 | 10 | 11 | class PullAndPushTest extends StatefulWidget{ 12 | 13 | @override 14 | State createState() { 15 | return new PullAndPushTestState(); 16 | } 17 | } 18 | 19 | ///PullAndPush可以使用默认的样式,在此样式的基础上可以使用default**系列的属性改变显示效果,也可以自定义RefreshBox的样式(footerRefreshBox or headerRefreshBox),也就是说可以定义其中一个,另一个用默认的,也可以全部自定义 20 | ///isPullEnable;isPushEnable属性可以控制RefreshBox 是否可用,无论是自定义的还是默认的 21 | ///PullAndPush can use the default style,Based on this style, you can use the properties of the default** series to change the display, 22 | ///You can also customize the style of the RefreshBox (footerRefreshBox or headerRefreshBox), which means you can define one of them, and the other can be customized by default or all. 23 | class PullAndPushTestState extends State with TickerProviderStateMixin{ 24 | List addStrs=["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]; 25 | List strs=["1","2","3","4","5","6","7","8","9","0"]; 26 | ScrollController controller=new ScrollController(); 27 | //For compatibility with ios ,must use RefreshAlwaysScrollPhysics ;为了兼容ios 必须使用RefreshAlwaysScrollPhysics 28 | ScrollPhysics scrollPhysics=new RefreshAlwaysScrollPhysics(); 29 | //使用系统的请求 30 | var httpClient = new HttpClient(); 31 | var url = "https://github.com/"; 32 | var _result=""; 33 | String customRefreshBoxIconPath="images/icon_arrow.png"; 34 | AnimationController customBoxWaitAnimation; 35 | int rotationAngle=0; 36 | String customHeaderTipText="快尼玛给老子松手!"; 37 | String defaultRefreshBoxTipText="快尼玛给老子松手!"; 38 | ///button等其他方式,通过方法调用触发下拉刷新 39 | TriggerPullController triggerPullController=new TriggerPullController(); 40 | 41 | 42 | @override 43 | void initState() { 44 | super.initState(); 45 | //这个是刷新时控件旋转的动画,用来使刷新的Icon动起来 46 | customBoxWaitAnimation=new AnimationController(duration: const Duration(milliseconds: 1000*100), vsync: this); 47 | //第一次layout后会被调用 48 | WidgetsBinding.instance.addPostFrameCallback((context){ 49 | print("addPostFrameCallback is invoke"); 50 | //triggerPullController.triggerPull(); 51 | }); 52 | } 53 | 54 | 55 | @override 56 | Widget build(BuildContext context) { 57 | return new Scaffold( 58 | appBar: new AppBar( 59 | title: new Text("上下拉刷新"), 60 | ), 61 | body: new PullAndPush( 62 | //如果你headerRefreshBox和footerRefreshBox全都自定义了,则default**系列的属性均无效,假如有一个RefreshBox是用默认的(在该RefreshBox Enable的情况下)则default**系列的属性均有效 63 | //If your headerRefreshBox and footerRefreshBox are all customizable,then the default** attributes of the series are invalid, 64 | // If there is a RefreshBox is the default(In the case of the RefreshBox Enable)then the default** attributes of the series are valid 65 | defaultRefreshBoxTipText: defaultRefreshBoxTipText, 66 | headerRefreshBox: _getCustomHeaderBox(), 67 | triggerPullController:triggerPullController, 68 | 69 | //你也可以自定义底部的刷新栏;you can customize the bottom refresh box 70 | animationStateChangedCallback:(AnimationStates animationStates,RefreshBoxDirectionStatus refreshBoxDirectionStatus){ 71 | _handleStateCallback( animationStates, refreshBoxDirectionStatus); 72 | }, 73 | listView: new ListView.builder( 74 | //ListView的Item 75 | itemCount: strs.length,//+2, 76 | controller: controller, 77 | physics: scrollPhysics, 78 | itemBuilder: (BuildContext context,int index){ 79 | return new Container( 80 | height: 35.0, 81 | child: new Center( 82 | child: new Text(strs[index],style: new TextStyle(fontSize: 18.0),), 83 | ), 84 | ); 85 | } 86 | ), 87 | loadData: (isPullDown) async{ 88 | await _loadData(isPullDown); 89 | }, 90 | scrollPhysicsChanged: (ScrollPhysics physics) { 91 | //这个不用改,照抄即可;This does not need to change,only copy it 92 | setState(() { 93 | scrollPhysics=physics; 94 | }); 95 | }, 96 | ) 97 | ); 98 | } 99 | 100 | 101 | 102 | Widget _getCustomHeaderBox(){ 103 | return new Container( 104 | color: Colors.grey, 105 | child: new Row( 106 | mainAxisAlignment: MainAxisAlignment.center, 107 | children: [ 108 | new Align( 109 | alignment: Alignment.centerLeft, 110 | child: new RotatedBox( 111 | quarterTurns: rotationAngle, 112 | child: new RotationTransition( //布局中加载时动画的weight 113 | child: new Image.asset( 114 | customRefreshBoxIconPath, 115 | height: 45.0, 116 | width: 45.0, 117 | fit:BoxFit.cover, 118 | ), 119 | turns: new Tween( 120 | begin: 100.0, 121 | end: 0.0 122 | ) 123 | .animate(customBoxWaitAnimation) 124 | ..addStatusListener((animationStatus) { 125 | if (animationStatus == AnimationStatus.completed) { 126 | customBoxWaitAnimation.repeat(); 127 | } 128 | } 129 | ), 130 | ), 131 | ), 132 | ), 133 | 134 | new Align( 135 | alignment: Alignment.centerRight, 136 | child:new ClipRect( 137 | child:new Text(customHeaderTipText,style: new TextStyle(fontSize: 18.0,color: Color(0xffe6e6e6)),), 138 | ), 139 | ), 140 | ], 141 | ) 142 | ); 143 | } 144 | 145 | void _handleStateCallback(AnimationStates animationStates,RefreshBoxDirectionStatus refreshBoxDirectionStatus){ 146 | switch (animationStates){ 147 | //RefreshBox高度达到50,上下拉刷新可用;RefreshBox height reached 50,the function of load data is available 148 | case AnimationStates.DragAndRefreshEnabled: 149 | setState(() { 150 | //3.141592653589793是弧度,角度为180度,旋转180度;3.141592653589793 is radians,angle is 180⁰,Rotate 180⁰ 151 | rotationAngle=2; 152 | }); 153 | break; 154 | 155 | //开始加载数据时;When loading data starts 156 | case AnimationStates.StartLoadData: 157 | setState(() { 158 | customRefreshBoxIconPath="images/refresh.png"; 159 | customHeaderTipText="正尼玛在拼命加载....."; 160 | }); 161 | customBoxWaitAnimation.forward(); 162 | break; 163 | 164 | //加载完数据时;RefreshBox会留在屏幕2秒,并不马上消失,这里可以提示用户加载成功或者失败 165 | // After loading the data,RefreshBox will stay on the screen for 2 seconds, not disappearing immediately,Here you can prompt the user to load successfully or fail. 166 | case AnimationStates.LoadDataEnd: 167 | customBoxWaitAnimation.reset(); 168 | setState(() { 169 | rotationAngle = 0; 170 | if(refreshBoxDirectionStatus==RefreshBoxDirectionStatus.PULL) { 171 | customRefreshBoxIconPath = "images/icon_cry.png"; 172 | customHeaderTipText = "加载失败!请重试"; 173 | }else if(refreshBoxDirectionStatus==RefreshBoxDirectionStatus.PUSH){ 174 | defaultRefreshBoxTipText="可提示用户加载成功Or失败"; 175 | } 176 | }); 177 | break; 178 | 179 | //RefreshBox已经消失,并且闲置;RefreshBox has disappeared and is idle 180 | case AnimationStates.RefreshBoxIdle: 181 | setState(() { 182 | rotationAngle=0; 183 | defaultRefreshBoxTipText=customHeaderTipText="快尼玛给老子松手!"; 184 | customRefreshBoxIconPath="images/icon_arrow.png"; 185 | }); 186 | break; 187 | } 188 | } 189 | 190 | Future _loadData(bool isPullDown) async{ 191 | try { 192 | var request = await httpClient.getUrl(Uri.parse(url)); 193 | var response = await request.close(); 194 | if (response.statusCode == HttpStatus.ok) { 195 | _result = await response.transform(utf8.decoder).join(); 196 | setState(() { 197 | //拿到数据后,对数据进行梳理 198 | if(isPullDown){ 199 | strs.clear(); 200 | strs.addAll(addStrs); 201 | }else{ 202 | strs.addAll(addStrs); 203 | } 204 | }); 205 | } else { 206 | _result = 'error code : ${response.statusCode}'; 207 | } 208 | } catch (exception) { 209 | _result = '网络异常'; 210 | } 211 | print(_result); 212 | } 213 | } 214 | 215 | 216 | 217 | 218 | 219 | 220 | -------------------------------------------------------------------------------- /lib/ui/secondpage/beziercurvedemo.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutterapp/components/waveprogressbar.dart'; 3 | 4 | class BezierCurveDemo extends StatefulWidget{ 5 | @override 6 | State createState() { 7 | 8 | return BezierCurveDemoState(); 9 | } 10 | 11 | } 12 | 13 | class BezierCurveDemoState extends State{ 14 | 15 | final TextEditingController _controller = new TextEditingController(); 16 | //默认初始值为0.0 17 | double waterHeight=0.0; 18 | WaterController waterController=WaterController(); 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | WidgetsBinding widgetsBinding=WidgetsBinding.instance; 24 | widgetsBinding.addPostFrameCallback((callback){ 25 | //这里写你想要显示的百分比 26 | waterController.changeProgressRate(0.82); 27 | }); 28 | } 29 | 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | 34 | return new Scaffold( 35 | resizeToAvoidBottomPadding: false, 36 | appBar: new AppBar( 37 | title: new Text("贝塞尔曲线测试"), 38 | ), 39 | body: new Column( 40 | children: [ 41 | new Row( 42 | children: [ 43 | new Text("高度调整: ", 44 | style: new TextStyle(fontSize: 20.0), 45 | ), 46 | new Container( 47 | width: 150.0, 48 | child: new TextField( 49 | controller: _controller, 50 | decoration: new InputDecoration( 51 | hintText: "请输入高度", 52 | ) 53 | ), 54 | ), 55 | new RaisedButton(onPressed: (){ 56 | print("waterHeight is ${_controller.toString()}"); 57 | FocusScope.of(context).requestFocus(FocusNode()); 58 | waterController.changeProgressRate(double.parse(_controller.text)); 59 | }, 60 | child: new Text("确定"), 61 | ), 62 | ], 63 | ), 64 | new Container( 65 | margin: EdgeInsets.only(top: 80.0), 66 | child: new Center( 67 | child: new WaveProgressBar( 68 | flowSpeed: 2.0, 69 | waveDistance:45.0, 70 | waterColor: Color(0xFF68BEFC), 71 | //strokeCircleColor: Color(0x50e16009), 72 | progressController: waterController, 73 | percentage: waterHeight, 74 | size: new Size (300,300), 75 | textStyle: new TextStyle( 76 | color:Color(0x15000000), 77 | fontSize: 60.0, 78 | fontWeight: FontWeight.bold), 79 | ), 80 | ), 81 | ), 82 | ], 83 | ), 84 | ); 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /lib/ui/secondpage/blendmode.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'dart:ui' as ui; 3 | 4 | class BlendModes extends StatefulWidget{ 5 | @override 6 | State createState() { 7 | return new BlendModeState(); 8 | } 9 | 10 | } 11 | 12 | class BlendModeState extends State{ 13 | 14 | final double sizes=200.0; 15 | BlendMode blendMode=BlendMode.dstOver; 16 | String title="BlendMode.dstOver"; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return new Scaffold( 21 | appBar: new AppBar( 22 | title: new Text(title), 23 | actions: [ 24 | new PopupMenuButton( 25 | onSelected: (value){ 26 | setState(() { 27 | blendMode=value; 28 | title=value.toString(); 29 | }); 30 | }, 31 | itemBuilder: (BuildContext context) => getItems(), 32 | ), 33 | ], 34 | ), 35 | body: new Center( 36 | child: new CustomPaint( 37 | size: new Size(sizes, sizes), 38 | painter: new BlendModePainter(blendMode), 39 | ), 40 | ), 41 | ); 42 | } 43 | 44 | 45 | List> getItems(){ 46 | return >[ 47 | const PopupMenuItem( 48 | value: BlendMode.difference, 49 | child: Text('BlendMode.difference'), 50 | ), 51 | const PopupMenuItem( 52 | value: BlendMode.dst, 53 | child: Text('BlendMode.dst'), 54 | ), 55 | const PopupMenuItem( 56 | value: BlendMode.dstATop, 57 | child: Text('BlendMode.dstATop'), 58 | ), 59 | const PopupMenuItem( 60 | value: BlendMode.dstIn, 61 | child: Text('BlendMode.dstIn'), 62 | ), 63 | const PopupMenuItem( 64 | value: BlendMode.dstOut, 65 | child: Text('BlendMode.dstOut'), 66 | ), 67 | const PopupMenuItem( 68 | value: BlendMode.dstOver, 69 | child: Text('BlendMode.dstOver'), 70 | ), 71 | const PopupMenuItem( 72 | value: BlendMode.src, 73 | child: Text('BlendMode.src'), 74 | ), 75 | const PopupMenuItem( 76 | value: BlendMode.srcATop, 77 | child: Text('BlendMode.srcATop'), 78 | ), 79 | const PopupMenuItem( 80 | value: BlendMode.srcIn, 81 | child: Text('BlendMode.srcIn'), 82 | ), 83 | const PopupMenuItem( 84 | value: BlendMode.srcOut, 85 | child: Text('BlendMode.srcOut'), 86 | ), 87 | const PopupMenuItem( 88 | value: BlendMode.srcOver, 89 | child: Text('BlendMode.srcOver'), 90 | ), 91 | const PopupMenuItem( 92 | value: BlendMode.exclusion, 93 | child: Text('BlendMode.exclusion'), 94 | ), 95 | const PopupMenuItem( 96 | value: BlendMode.hardLight, 97 | child: Text('BlendMode.hardLight'), 98 | ), 99 | const PopupMenuItem( 100 | value: BlendMode.hue, 101 | child: Text('BlendMode.hue'), 102 | ), 103 | const PopupMenuItem( 104 | value: BlendMode.lighten, 105 | child: Text('BlendMode.lighten'), 106 | ), 107 | const PopupMenuItem( 108 | value: BlendMode.luminosity, 109 | child: Text('BlendMode.luminosity'), 110 | ), 111 | const PopupMenuItem( 112 | value: BlendMode.modulate, 113 | child: Text('BlendMode.modulate'), 114 | ), 115 | const PopupMenuItem( 116 | value: BlendMode.multiply, 117 | child: Text('BlendMode.multiply'), 118 | ), 119 | const PopupMenuItem( 120 | value: BlendMode.overlay, 121 | child: Text('BlendMode.overlay'), 122 | ), 123 | const PopupMenuItem( 124 | value: BlendMode.plus, 125 | child: Text('BlendMode.plus'), 126 | ), 127 | const PopupMenuItem( 128 | value: BlendMode.saturation, 129 | child: Text('BlendMode.saturation'), 130 | ), 131 | const PopupMenuItem( 132 | value: BlendMode.screen, 133 | child: Text('BlendMode.screen'), 134 | ), 135 | const PopupMenuItem( 136 | value: BlendMode.softLight, 137 | child: Text('BlendMode.softLight'), 138 | ),const PopupMenuItem( 139 | value: BlendMode.clear, 140 | child: Text('BlendMode.clear'), 141 | ), 142 | const PopupMenuItem( 143 | value: BlendMode.color, 144 | child: Text('BlendMode.color'), 145 | ), 146 | const PopupMenuItem( 147 | value: BlendMode.colorBurn, 148 | child: Text('BlendMode.colorBurn'), 149 | ), 150 | const PopupMenuItem( 151 | value: BlendMode.colorDodge, 152 | child: Text('BlendMode.colorDodge'), 153 | ), 154 | const PopupMenuItem( 155 | value: BlendMode.darken, 156 | child: Text('BlendMode.darken'), 157 | ), 158 | 159 | // const PopupMenuItem( 160 | // value: BlendMode.values, 161 | // child: Text('BlendMode.values'), 162 | // ), 163 | const PopupMenuItem( 164 | value: BlendMode.xor, 165 | child: Text('BlendMode.xor'), 166 | ), 167 | ]; 168 | } 169 | } 170 | 171 | class BlendModePainter extends CustomPainter{ 172 | 173 | final BlendMode blendMode; 174 | 175 | BlendModePainter(this.blendMode); 176 | 177 | @override 178 | void paint(Canvas canvas, Size size) { 179 | Paint paint=new Paint(); 180 | Paint layerPaint=new Paint(); 181 | 182 | 183 | canvas.saveLayer(new Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), layerPaint); 184 | //paint.color=Color(0xff2196f3); 185 | paint.shader= ui.Gradient.linear(new Offset(0.0, 0.0), new Offset(150.0, 150.0), [Colors.red,Colors.green,Colors.blue],[0.0,0.5,1.0]); 186 | canvas.drawRect(new Rect.fromLTRB(0.0, 0.0, 150.0, 150.0), paint); 187 | paint.shader=null; 188 | 189 | 190 | layerPaint.blendMode=blendMode ; 191 | 192 | canvas.saveLayer(new Rect.fromLTRB(0.0, 0.0, 200.0, 200.0), layerPaint); 193 | canvas.save(); 194 | canvas.translate(125.0, 125.0); 195 | paint.color=Colors.yellow; 196 | canvas.drawCircle(new Offset(0.0, 0.0), 75.0, paint); 197 | canvas.restore(); 198 | canvas.restore(); 199 | canvas.restore(); 200 | } 201 | 202 | @override 203 | bool shouldRepaint(CustomPainter oldDelegate) { 204 | return true; 205 | } 206 | 207 | } -------------------------------------------------------------------------------- /lib/ui/secondpage/bubbles.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutterapp/bin/bubblebin.dart'; 5 | 6 | class Bubbles extends StatefulWidget{ 7 | @override 8 | State createState() { 9 | return new BubblesState(); 10 | } 11 | 12 | } 13 | 14 | class BubblesState extends State{ 15 | 16 | List _bubbles=new List(); 17 | Random _random=new Random(); 18 | double xBlankSpace=0.2; 19 | double _maxSpeed=6.0; 20 | double _fixedHeight; 21 | double _disappearHeight; 22 | double maxRadius=25.0; 23 | double screenWidth; 24 | double screenHeight; 25 | WidgetsBinding widgetsBinding; 26 | final List _colors=[Colors.green,Color(0XFF66BB6A),Color(0XFF4CAF50),Color(0XFF43A047),Color(0XFF388E3C),Color(0XFF2E7D32), 27 | Color(0XFF1B5E20),Color(0XFF00C853),Color(0XFF00E676),Color(0XFF64DD17),Color(0XFF76FF03)]; 28 | 29 | @override 30 | void initState() { 31 | super.initState(); 32 | //print("initState be invoke"); 33 | widgetsBinding=WidgetsBinding.instance; 34 | widgetsBinding.addPostFrameCallback((callback){ 35 | widgetsBinding.addPersistentFrameCallback((callback){ 36 | print("addPersistentFrameCallback be invoke"); 37 | if(mounted){ 38 | setState(() { 39 | //TODO 先将数据改变,再添加新数据 40 | BubbleBin bubbleBin; 41 | for (int i=0;i<_bubbles.length;i++){ 42 | bubbleBin=_bubbles[i]; 43 | if(bubbleBin.yPositionxBlankSpace&&xPositionRandom<1.0-xBlankSpace 65 | && yPositionRandom>0.2 66 | && radiusRandom>0.2 67 | && speedRandom>0.2 68 | && _bubbles.length<10){ 69 | _disappearHeight=screenHeight-(_fixedHeight+_fixedHeight*_getPercentageOfDisappearHeight(yPositionRandom)); 70 | BubbleBin bin=new BubbleBin( 71 | radius:maxRadius*radiusRandom, 72 | color: _colors[intRandom], 73 | yPosition: screenHeight, 74 | disappearHeight: _disappearHeight, 75 | transparency: 0XFF, 76 | xPosition: screenWidth*xPositionRandom, 77 | speed: _maxSpeed*speedRandom); 78 | _bubbles.add(bin); 79 | } 80 | }); 81 | widgetsBinding.scheduleFrame(); 82 | } 83 | 84 | }); 85 | }); 86 | } 87 | 88 | @override 89 | void didChangeDependencies() { 90 | super.didChangeDependencies(); 91 | Size size=MediaQuery.of(context).size; 92 | screenWidth=size.width; 93 | screenHeight=size.height; 94 | _fixedHeight=screenHeight/2.0; 95 | } 96 | 97 | double _getPercentageOfDisappearHeight(double doubleRandom){ 98 | double percentageOfDisappearHeight; 99 | if(doubleRandom>0.3){ 100 | percentageOfDisappearHeight=_getPercentageOfDisappearHeight(doubleRandom/2); 101 | }else{ 102 | percentageOfDisappearHeight=doubleRandom; 103 | } 104 | return percentageOfDisappearHeight; 105 | } 106 | 107 | 108 | 109 | @override 110 | Widget build(BuildContext context) { 111 | return new Scaffold( 112 | appBar: new AppBar( 113 | title: new Text("泡泡"), 114 | ), 115 | body: new Container( 116 | child: new CustomPaint( 117 | size: new Size(screenWidth,screenHeight), 118 | painter: new BubblesPainter(_bubbles), 119 | ), 120 | ), 121 | ); 122 | } 123 | } 124 | 125 | class BubblesPainter extends CustomPainter{ 126 | 127 | final List _bubbles; 128 | 129 | BubblesPainter(this._bubbles); 130 | 131 | @override 132 | void paint(Canvas canvas, Size size) { 133 | Paint paint=new Paint(); 134 | paint.style=PaintingStyle.fill; 135 | BubbleBin bin; 136 | Offset offset; 137 | Color color; 138 | for (int i=0;i<_bubbles.length;i++){ 139 | bin=_bubbles[i]; 140 | color=bin.color; 141 | paint.color=color.withAlpha(bin.transparency); 142 | offset=new Offset(bin.xPosition, bin.yPosition); 143 | canvas.drawCircle(offset , bin.radius, paint); 144 | } 145 | } 146 | 147 | @override 148 | bool shouldRepaint(CustomPainter oldDelegate) { 149 | return true; 150 | } 151 | } -------------------------------------------------------------------------------- /lib/ui/secondpage/dashboard.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | 6 | 7 | class DashBoard extends StatefulWidget{ 8 | @override 9 | State createState() { 10 | return new DashBoardState(); 11 | } 12 | } 13 | 14 | 15 | class DashBoardState extends State{ 16 | 17 | final platform = const MethodChannel('com.flutter.lgyw/sensor'); 18 | bool _isGetPressure=false; 19 | int pressures=0;final double wholeCirclesRadian=6.283185307179586; 20 | ///虽然一个圆被分割为160份,但是只显示120份 21 | final int tableCount=160; 22 | Size dashBoardSize; 23 | double tableSpace; 24 | Picture _pictureBackGround; 25 | Picture _pictureIndicator; 26 | 27 | @override 28 | void initState() { 29 | super.initState(); 30 | dashBoardSize=new Size(300.0,300.0); 31 | tableSpace=wholeCirclesRadian/tableCount; 32 | _pictureBackGround=DashBoardTablePainter(tableSpace,dashBoardSize).getBackGround(); 33 | _pictureIndicator=IndicatorPainter(dashBoardSize).drawIndicator(); 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return new Scaffold( 39 | appBar: new AppBar( 40 | title: new Text("汽车仪表盘"), 41 | ), 42 | body: new Center( 43 | child:GestureDetector( 44 | onPanDown:(DragDownDetails dragDownDetails){ 45 | _isGetPressure=true; 46 | boostSpeed(); 47 | }, 48 | onPanCancel: (){ 49 | handleEndEvent(); 50 | }, 51 | onPanEnd: (DragEndDetails dragEndDetails){ 52 | handleEndEvent(); 53 | }, 54 | child:new CustomPaint( 55 | size: dashBoardSize, 56 | painter: new DashBoardIndicatorPainter(pressures,tableSpace,_pictureBackGround,_pictureIndicator), 57 | ), 58 | ), 59 | ), 60 | ); 61 | } 62 | 63 | void boostSpeed() async { 64 | while (_isGetPressure){ 65 | if(pressures<120){ 66 | setState(() { 67 | pressures++; 68 | }); 69 | } 70 | await Future.delayed(new Duration(milliseconds: 30)); 71 | } 72 | } 73 | 74 | 75 | void handleEndEvent(){ 76 | _isGetPressure=false; 77 | bringDownSpeed(); 78 | } 79 | 80 | 81 | void bringDownSpeed() async { 82 | while (!_isGetPressure){ 83 | setState(() { 84 | pressures--; 85 | }); 86 | 87 | if(pressures<=0){ 88 | break; 89 | } 90 | await Future.delayed(new Duration(milliseconds: 30)); 91 | } 92 | } 93 | } 94 | 95 | 96 | class DashBoardIndicatorPainter extends CustomPainter{ 97 | 98 | final int speeds; 99 | double tableSpace; 100 | final Picture pictureBackGround; 101 | final Picture pictureIndicator; 102 | 103 | DashBoardIndicatorPainter(this.speeds,this.tableSpace,this.pictureBackGround,this.pictureIndicator); 104 | 105 | @override 106 | void paint(Canvas canvas, Size size) { 107 | canvas.drawPicture(pictureBackGround); 108 | drawIndicator( canvas, size); 109 | String text; 110 | if(speeds<100){ 111 | text=(speeds*2).toString()+"KM/H"; 112 | }else{ 113 | int s=speeds-100; 114 | text=(100*2+s*3).toString()+"KM/H"; 115 | } 116 | drawSpeendOnDashBoard(text,canvas, size); 117 | } 118 | 119 | @override 120 | bool shouldRepaint(CustomPainter oldDelegate) { 121 | return true; 122 | } 123 | 124 | ///画实时得速度值到面板上 125 | void drawSpeendOnDashBoard(String text,Canvas canvas,Size size){ 126 | double halfHeight=size.height/2; 127 | double halfWidth=size.width/2; 128 | canvas.save(); 129 | canvas.translate(halfWidth, halfHeight); 130 | 131 | TextPainter textPainter = new TextPainter(); 132 | textPainter.textDirection = TextDirection.ltr; 133 | textPainter.text = new TextSpan(text: text, style: new TextStyle(color:Colors.deepOrangeAccent,fontSize: 25.0,fontStyle: FontStyle.italic,fontWeight: FontWeight.bold)); 134 | textPainter.layout(); 135 | double textStarPositionX = -textPainter.size.width / 2; 136 | double textStarPositionY = 73; 137 | textPainter.paint(canvas, new Offset(textStarPositionX, textStarPositionY)); 138 | 139 | canvas.restore(); 140 | } 141 | 142 | 143 | 144 | ///画速度指针 145 | void drawIndicator(Canvas canvas, Size size){ 146 | double halfHeight=size.height/2; 147 | double halfWidth=size.width/2; 148 | 149 | canvas.save(); 150 | canvas.translate(halfWidth, halfHeight); 151 | canvas.rotate((-60+speeds)*tableSpace); 152 | canvas.translate(-halfWidth, -halfHeight); 153 | 154 | canvas.drawPicture(pictureIndicator); 155 | 156 | canvas.restore(); 157 | } 158 | } 159 | 160 | 161 | class IndicatorPainter { 162 | 163 | final PictureRecorder _recorder = PictureRecorder(); 164 | final Size size; 165 | 166 | IndicatorPainter(this.size); 167 | 168 | ///画速度指针 169 | Picture drawIndicator(){ 170 | Canvas canvas=Canvas(_recorder); 171 | canvas.clipRect(new Rect.fromLTWH(0.0, 0.0, size.width, size.height)); 172 | 173 | double halfHeight=size.height/2; 174 | double halfWidth=size.width/2; 175 | Path path=new Path(); 176 | path.moveTo(-2.5, 20); 177 | path.lineTo(2.5, 20); 178 | path.lineTo(6.0, -30); 179 | path.lineTo(0.5, -halfHeight+8); 180 | path.lineTo(-0.5, -halfHeight+8); 181 | path.lineTo(-6.0, -30); 182 | path.close(); 183 | canvas.save(); 184 | 185 | canvas.translate(halfWidth, halfHeight); 186 | 187 | Paint paint=new Paint(); 188 | paint.color=Colors.red; 189 | paint.style=PaintingStyle.fill; 190 | 191 | canvas.drawPath(path, paint); 192 | 193 | paint.color=Colors.black; 194 | canvas.drawCircle(new Offset(0.0,0.0), 6, paint); 195 | 196 | canvas.restore(); 197 | return _recorder.endRecording(); 198 | } 199 | } 200 | 201 | 202 | 203 | 204 | class DashBoardTablePainter { 205 | 206 | final double tableSpace; 207 | var speedTexts=["0","20","40","60","80","100","120","140","160","180","200","230","260"]; 208 | final Size size; 209 | final PictureRecorder _recorder = PictureRecorder(); 210 | 211 | DashBoardTablePainter(this.tableSpace,this.size); 212 | 213 | Picture getBackGround() { 214 | Canvas canvas=Canvas(_recorder); 215 | canvas.clipRect(new Rect.fromLTWH(0.0, 0.0, size.width, size.height)); 216 | drawTable( canvas, size); 217 | return _recorder.endRecording(); 218 | } 219 | 220 | 221 | ///画仪表盘的表格 222 | void drawTable(Canvas canvas, Size size){ 223 | canvas.save(); 224 | double halfWidth=size.width/2; 225 | double halfHeight=size.height/2; 226 | canvas.translate(halfWidth, halfHeight); 227 | 228 | Paint paintMain=new Paint(); 229 | paintMain.color=Colors.blue; 230 | paintMain.strokeWidth=2.5; 231 | paintMain.style=PaintingStyle.fill; 232 | 233 | 234 | Paint paintOther=new Paint(); 235 | paintOther.color=Colors.blue; 236 | paintOther.strokeWidth=1; 237 | paintOther.style=PaintingStyle.fill; 238 | 239 | drawLongLine(canvas,paintMain,halfHeight,speedTexts[6]); 240 | 241 | canvas.save(); 242 | for(int i=61;i<=120;i++){ 243 | canvas.rotate(tableSpace); 244 | if(i%10==0){ 245 | int a=(i/10).ceil(); 246 | changePaintColors(paintMain,i); 247 | drawLongLine(canvas,paintMain,halfHeight,speedTexts[a]); 248 | }else if(i%5==0){ 249 | changePaintColors(paintMain,i); 250 | drawMiddleLine(canvas,paintMain,halfHeight); 251 | }else{ 252 | changePaintColors(paintOther,i); 253 | drawSmallLine(canvas,paintOther,halfHeight); 254 | } 255 | } 256 | canvas.restore(); 257 | 258 | 259 | canvas.save(); 260 | for(int i=59;i>=0;i--){ 261 | canvas.rotate(-tableSpace); 262 | if(i%10==0){ 263 | int a=(i/10).ceil(); 264 | changePaintColors(paintMain,i); 265 | drawLongLine(canvas,paintMain,halfHeight,speedTexts[a]); 266 | }else if(i%5==0){ 267 | changePaintColors(paintMain,i); 268 | drawMiddleLine(canvas,paintMain,halfHeight); 269 | }else{ 270 | changePaintColors(paintOther,i); 271 | drawSmallLine(canvas,paintOther,halfHeight); 272 | } 273 | } 274 | canvas.restore(); 275 | 276 | canvas.restore(); 277 | } 278 | 279 | 280 | void changePaintColors(Paint paint,int value){ 281 | if(value<=20){ 282 | paint.color=Colors.green; 283 | }else if(value<80){ 284 | paint.color=Colors.blue; 285 | }else{ 286 | paint.color=Colors.red; 287 | } 288 | } 289 | 290 | ///画仪表盘上的长线 291 | void drawLongLine(Canvas canvas,Paint paintMain,double halfHeight,String text){ 292 | canvas.drawLine(new Offset(0.0, -halfHeight), new Offset(0.0, -halfHeight+15), paintMain); 293 | 294 | TextPainter textPainter = new TextPainter(); 295 | textPainter.textDirection = TextDirection.ltr; 296 | textPainter.text = new TextSpan(text: text, style: new TextStyle(color:paintMain.color,fontSize: 15.5,)); 297 | textPainter.layout(); 298 | double textStarPositionX = -textPainter.size.width / 2; 299 | double textStarPositionY = -halfHeight+19; 300 | textPainter.paint(canvas, new Offset(textStarPositionX, textStarPositionY)); 301 | } 302 | 303 | 304 | void drawMiddleLine(Canvas canvas,Paint paintMain,double halfHeight){ 305 | canvas.drawLine(new Offset(0.0, -halfHeight), new Offset(0.0, -halfHeight+10), paintMain); 306 | } 307 | 308 | 309 | ///画短线 310 | void drawSmallLine(Canvas canvas,Paint paintOther,double halfHeight){ 311 | canvas.drawLine(new Offset(0.0, -halfHeight), new Offset(0.0, -halfHeight+7), paintOther); 312 | } 313 | 314 | } 315 | 316 | -------------------------------------------------------------------------------- /lib/ui/secondpage/drawablestarttext.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutterapp/components/drawablestarttext.dart'; 3 | 4 | class DrawableStartTextDemo extends StatelessWidget{ 5 | @override 6 | Widget build(BuildContext context) { 7 | 8 | return new Scaffold( 9 | appBar: new AppBar( 10 | title: new Text("DrawableStartText"), 11 | ), 12 | body: new Center( 13 | child: new Container( 14 | child: new DrawableStartText( 15 | assetImage: "images/tianmao.jpg", 16 | text: " 莫顿 全自动感应壁挂式酒精喷雾式手消毒器 手消毒机杀菌净手器", 17 | textStyle: new TextStyle(fontSize: 17.0), 18 | ), 19 | ), 20 | ), 21 | ); 22 | } 23 | } 24 | 25 | 26 | -------------------------------------------------------------------------------- /lib/ui/secondpage/loginanimdemo.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'dart:convert'; 3 | 4 | import 'package:flutter/material.dart'; 5 | 6 | import 'package:flutterapp/components/loginanimation.dart'; 7 | 8 | class LoginAnimationDemo extends StatefulWidget{ 9 | 10 | final String loginTip="登陆"; 11 | final double width=200.0,height=40.0; 12 | 13 | @override 14 | State createState() { 15 | return new LoginAnimationDemoState(); 16 | } 17 | 18 | } 19 | 20 | class LoginAnimationDemoState extends State{ 21 | 22 | //使用系统的请求 23 | var httpClient = new HttpClient(); 24 | var url = "https://github.com/"; 25 | var _result=""; 26 | final LoginErrorMessageController loginErrorMessageController=LoginErrorMessageController(); 27 | 28 | @override 29 | void initState() { 30 | super.initState(); 31 | } 32 | 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | 37 | return new Scaffold( 38 | appBar: new AppBar( 39 | title: new Text("登陆按钮动画"), 40 | ), 41 | body: new Center( 42 | child:new Container( 43 | child:new AnimatedLoginButton( 44 | loginErrorMessageController:loginErrorMessageController, 45 | onTap: () async { 46 | try { 47 | var request = await httpClient.getUrl(Uri.parse(url)); 48 | var response = await request.close(); 49 | if (response.statusCode == HttpStatus.ok) { 50 | _result = await response.transform(utf8.decoder).join(); 51 | 52 | //拿到数据后,对数据进行梳理 53 | loginErrorMessageController.showErrorMessage("网络异常"); 54 | 55 | } else { 56 | _result = 'ERROR CODE: ${response.statusCode}'; 57 | loginErrorMessageController.showErrorMessage("网络异常 $_result"); 58 | } 59 | } catch (exception) { 60 | _result = '网络异常'; 61 | loginErrorMessageController.showErrorMessage("网络异常"); 62 | } 63 | print(_result); 64 | }, 65 | ), 66 | ), 67 | ), 68 | ); 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /lib/ui/secondpage/radarchartdemo.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutterapp/components/radarchart.dart'; 3 | 4 | class RadarChartDemo extends StatelessWidget{ 5 | @override 6 | Widget build(BuildContext context) { 7 | 8 | return new Scaffold( 9 | appBar: new AppBar( 10 | title: new Text("雷达图"), 11 | ), 12 | body: new Center( 13 | child: new RadarChart(), 14 | ), 15 | ); 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /lib/ui/secondpage/secondPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutterapp/bin/appinfobin.dart'; 3 | import 'package:flutterapp/ui/secondpage/beziercurvedemo.dart'; 4 | import 'package:flutterapp/ui/secondpage/blendmode.dart'; 5 | import 'package:flutterapp/ui/secondpage/bubbles.dart'; 6 | import 'package:flutterapp/ui/secondpage/dashboard.dart'; 7 | import 'package:flutterapp/ui/secondpage/drawablestarttext.dart'; 8 | import 'package:flutterapp/ui/secondpage/loginanimdemo.dart'; 9 | import 'package:flutterapp/ui/secondpage/radarchartdemo.dart'; 10 | import 'package:flutterapp/ui/secondpage/timepicker.dart'; 11 | 12 | class SecondPage extends StatefulWidget{ 13 | 14 | @override 15 | State createState() { 16 | return new SecondPageState(); 17 | } 18 | 19 | } 20 | 21 | class SecondPageState extends State{ 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return new Scaffold( 26 | appBar: new AppBar( 27 | title: new Text("CustomWidght",style: new TextStyle(fontSize: 18.0),), 28 | actions: [ 29 | new IconButton( 30 | icon: new Icon(Icons.search), 31 | onPressed: null, 32 | iconSize: 29.0, 33 | disabledColor: Colors.white,) 34 | ] 35 | ,), 36 | body: new Container( 37 | // ignore: conflicting_dart_import 38 | child: new ListViewWidgets(), 39 | ) 40 | ); 41 | } 42 | } 43 | 44 | 45 | class ListViewWidgets extends StatefulWidget{ 46 | 47 | var constantList = [ 48 | new AppInfoBin("IOSPicker", "2018-07-16 16:16", "自定义仿IOS时间选择器",false), 49 | new AppInfoBin("LoginAnimation", "2018-11-26 10:05", "登陆按钮的动画",false), 50 | new AppInfoBin("BlendMode", "2018-11-28 10:52", "BlendMode测试",false), 51 | new AppInfoBin("DashBoard", "2018-11-29 14:34", "汽车仪表盘模拟",false), 52 | new AppInfoBin("Bubbles", "2018-12-07 10:38", "泡泡往上冒的动画",false), 53 | new AppInfoBin("BezierCurve", "2018-12-26 16:00", "水波纹效果",false), 54 | new AppInfoBin("RadarChart", "2019-01-02 15:33", "雷达图(蜘蛛网图)",false), 55 | new AppInfoBin("DrawableStartText", "2019-01-10 09:27", "文字的开头添加标签",false), 56 | ]; 57 | 58 | @override 59 | State createState() { 60 | return new ListState(); 61 | } 62 | 63 | } 64 | 65 | class ListState extends State{ 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | return new ListView.builder( 70 | itemCount: widget.constantList.length, 71 | itemBuilder: (BuildContext context,int index){ 72 | return new GestureDetector( 73 | onTapDown: (TapDownDetails details){ 74 | setState(() { 75 | widget.constantList[index].isTapDown=true; 76 | }); 77 | }, 78 | onTapCancel: (){ 79 | setState(() { 80 | widget.constantList[index].isTapDown=false; 81 | }); 82 | }, 83 | onTapUp:(TapUpDetails details){ 84 | setState(() { 85 | widget.constantList[index].isTapDown=false; 86 | }); 87 | if(index==0){ 88 | Navigator.of(context).push(new PageRouteBuilder( 89 | pageBuilder: (BuildContext context, Animation animation, Animation secondaryAnimation) { 90 | return new Timepicker(); 91 | }, 92 | transitionsBuilder: (BuildContext context, Animation animation, Animation secondaryAnimation, Widget child) { 93 | return ScaleTransition( 94 | scale: new Tween( 95 | begin: 0.3, 96 | end: 1.0, 97 | ).animate(animation), 98 | child: child, 99 | ); 100 | }, 101 | )); 102 | }else if(index==1){ 103 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) { 104 | return new LoginAnimationDemo(); 105 | })); 106 | }else if(index==2){ 107 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) { 108 | return new BlendModes(); 109 | })); 110 | }else if(index==3){ 111 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) { 112 | return new DashBoard(); 113 | })); 114 | }else if(index==4){ 115 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) { 116 | return new Bubbles(); 117 | })); 118 | }else if(index==5){ 119 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) { 120 | return new BezierCurveDemo(); 121 | })); 122 | }else if(index==6){ 123 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) { 124 | return new RadarChartDemo(); 125 | })); 126 | }else if(index==7){ 127 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) { 128 | return new DrawableStartTextDemo(); 129 | })); 130 | } 131 | }, 132 | child:new Card( 133 | color: widget.constantList[index].isTapDown?Color(0xFFF4CB28):null, 134 | child: new Container( 135 | padding: new EdgeInsets.all(10.0), 136 | child: new ListTile( 137 | subtitle: new Container( 138 | child: new Column( 139 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 140 | children: [ 141 | new Row( 142 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 143 | children: [ 144 | new Text(widget.constantList[index].title,style: new TextStyle(fontWeight: FontWeight.bold,fontSize: 16.0,color: Colors.black),) 145 | ], 146 | ), 147 | new Row( 148 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 149 | children: [ 150 | new Row( 151 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 152 | crossAxisAlignment: CrossAxisAlignment.center, 153 | children: [ 154 | new Text("时间",style: new TextStyle(fontSize: 13.0)), 155 | new Text(widget.constantList[index].times,style: new TextStyle(fontSize: 13.0)), 156 | ], 157 | ) 158 | ], 159 | ), 160 | new Row( 161 | children: [ 162 | new Expanded( 163 | child:new Container( 164 | padding: const EdgeInsets.fromLTRB(0.0, 8.0, 0.0, 2.0), 165 | child: new Text(widget.constantList[index].content,maxLines: 1,overflow: TextOverflow.ellipsis,), 166 | ), 167 | ) 168 | ], 169 | ) 170 | ], 171 | ), 172 | ), 173 | trailing: new Icon(Icons.keyboard_arrow_right,color: Colors.grey,), 174 | ), 175 | ), 176 | ) 177 | ); 178 | },); 179 | } 180 | 181 | } -------------------------------------------------------------------------------- /lib/ui/secondpage/timepicker.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/physics.dart'; 5 | 6 | class Timepicker extends StatefulWidget{ 7 | @override 8 | State createState() { 9 | return new TimepickerState(); 10 | } 11 | 12 | } 13 | 14 | class TimepickerState extends State with SingleTickerProviderStateMixin{ 15 | 16 | List hours; 17 | AnimationController controller; 18 | Animation animation; 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | controller = new AnimationController(duration: const Duration(milliseconds: 1800), vsync: this); 24 | hours=new List(); 25 | for(int i=0;i<24;i++){ 26 | hours.add(i.toString()+"小时"); 27 | } 28 | } 29 | 30 | 31 | double start=0.0; 32 | double offset=0.0; 33 | TextPaintCanvas paintCanvas; 34 | FrictionSimulation frictionSimulation; 35 | AnimationStatusListener statusListener; 36 | VoidCallback voidCallback; 37 | int downTime; 38 | 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return new Scaffold( 43 | backgroundColor: Colors.white70, 44 | appBar: new AppBar( 45 | title: new Text("时间选择器"), 46 | ), 47 | body: new Builder(builder: (BuildContext contextx){ 48 | return new Center( 49 | child:new Container( 50 | width: 80.0, 51 | height: 130.0, 52 | child: new GestureDetector( 53 | onVerticalDragDown: (getureDragDownCallback){ 54 | if(!controller.isCompleted) { 55 | paintCanvas.changeFirstCount(); 56 | animation.removeListener(voidCallback); 57 | animation.removeStatusListener(statusListener); 58 | controller.reset(); 59 | } 60 | }, 61 | onVerticalDragStart: (getureDragStartCallback){ 62 | downTime=DateTime.now().millisecondsSinceEpoch; 63 | start=getureDragStartCallback.globalPosition.dy; 64 | }, 65 | /******************************************************************************************************************/ 66 | onVerticalDragEnd: (getureDragEndCallback){ 67 | Velocity velo=getureDragEndCallback.velocity; 68 | if(velo.pixelsPerSecond.dy.isNaN){ 69 | return; 70 | } 71 | double lastOffset=offset; 72 | double distance=(velo.pixelsPerSecond.dy).abs()/10; 73 | if(DateTime.now().millisecondsSinceEpoch-downTime<300&&lastOffset<15&&distance==0.0){ 74 | return; 75 | } 76 | double controlOffset = paintCanvas.getOffset(distance + lastOffset.abs()); 77 | if(controlOffset.isNaN){ 78 | return; 79 | } 80 | if(distance==0.0){ 81 | distance=paintCanvas.getItemHeight()-controlOffset; 82 | } 83 | 84 | 85 | double scrollOffset; 86 | if(velo.pixelsPerSecond.dy==0.0){ 87 | frictionSimulation=new FrictionSimulation.through(0.0,distance+0.1,20.0,0.0); 88 | scrollOffset=frictionSimulation.timeAtX(distance); 89 | }else{ 90 | frictionSimulation=new FrictionSimulation.through(0.0,distance,20.0,0.0); 91 | scrollOffset=frictionSimulation.timeAtX(distance-controlOffset); 92 | } 93 | 94 | 95 | animation = new Tween(begin: 0.0, end: scrollOffset).animate(controller); 96 | voidCallback=(){ 97 | if(animation.value==0.0){ 98 | return; 99 | } 100 | if(offset<0){ 101 | offset=-(frictionSimulation.x(animation.value))+lastOffset; 102 | }else{ 103 | offset=frictionSimulation.x(animation.value)+lastOffset; 104 | } 105 | if(offset.isNaN){ 106 | return; 107 | } 108 | setState(() { 109 | paintCanvas.changeDatas(offset,hours); 110 | }); 111 | }; 112 | statusListener=(animationStatus){ 113 | if(animationStatus==AnimationStatus.completed){ 114 | //offset=0.0; 115 | paintCanvas.changeFirstCount(); 116 | animation.removeListener(voidCallback); 117 | animation.removeStatusListener(statusListener); 118 | controller.reset(); 119 | Scaffold.of(contextx).showSnackBar(new SnackBar(content: new Text(hours[3]),duration: new Duration(seconds: 1),)); 120 | }else if(animationStatus==AnimationStatus.forward){ 121 | //frictionSimulation.x(430.0); 122 | } 123 | }; 124 | animation.addStatusListener(statusListener); 125 | animation.addListener(voidCallback); 126 | controller.forward(); 127 | }, 128 | /******************************************************************************************************************/ 129 | onVerticalDragUpdate: (getureDragUpdateCallback){ 130 | double point =getureDragUpdateCallback.globalPosition.dy; 131 | offset=start-point; 132 | if(offset.abs()<15){ 133 | return; 134 | } 135 | setState(() { 136 | paintCanvas.changeDatas(offset,hours); 137 | }); 138 | }, 139 | onVerticalDragCancel: (){ 140 | 141 | }, 142 | child: new CustomPaint( 143 | painter: paintCanvas=new TextPaintCanvas(-offset,hours), 144 | ) 145 | ), 146 | ), 147 | ); 148 | }), 149 | ); 150 | } 151 | } 152 | 153 | class TextPaintCanvas extends CustomPainter{ 154 | 155 | TextPaintCanvas(this.offset,this.hours); 156 | 157 | double offset; 158 | static double maginsHeight=0.0; 159 | static double smallTextHright=0.0; 160 | static int firstCount=0; 161 | List hours; 162 | static double preOffset=0.0; 163 | static int modle=0; 164 | 165 | 166 | void changeFirstCount(){ 167 | firstCount=0; 168 | preOffset=0.0; 169 | firstCount=0; 170 | modle=0; 171 | } 172 | 173 | double getOffset(double lastOffset){ 174 | if(maginsHeight!=0.0) { 175 | return lastOffset % (smallTextHright + maginsHeight); 176 | } 177 | return 0.0; 178 | } 179 | 180 | double getItemHeight(){ 181 | if(maginsHeight!=0.0) { 182 | return smallTextHright + maginsHeight; 183 | } 184 | return 0.0; 185 | } 186 | 187 | void changeDatas(double offsets,List hours){ 188 | if(maginsHeight!=0.0) { 189 | int a = offsets ~/ (smallTextHright + maginsHeight); 190 | if (firstCount != a) { 191 | //向上滑 192 | if (preOffset-offsets<0) { 193 | //移除第一个,加到末尾 194 | if(getOffset(offsets)==0.0){ 195 | return; 196 | } 197 | String first = hours.first; 198 | hours.removeAt(0); 199 | hours.add(first); 200 | firstCount ++; 201 | preOffset=offsets; 202 | //向下滑 203 | } else if (preOffset-offsets>0) { 204 | //除去最后一个。加到前面 205 | String last = hours.last; 206 | hours.removeLast(); 207 | hours.insert(0, last); 208 | firstCount --; 209 | preOffset=offsets; 210 | } 211 | }else if(firstCount == a&&firstCount==0){ 212 | if(offsets>0&&modle!=1){ 213 | modle=1; 214 | String first = hours.first; 215 | hours.removeAt(0); 216 | hours.add(first); 217 | }else if(offsets<0&&modle!=-1){ 218 | modle=-1; 219 | String last = hours.last; 220 | hours.removeLast(); 221 | hours.insert(0, last); 222 | } 223 | } 224 | } 225 | } 226 | 227 | void parseOffset(){ 228 | if(maginsHeight!=0.0) { 229 | offset = offset % (smallTextHright + maginsHeight); 230 | //print(offset); 231 | } 232 | } 233 | 234 | 235 | double getRadian(double itemOffset,double controlHeight,double maginsHeight,double smallTextHright){ 236 | double radian; 237 | double radius=controlHeight/2+smallTextHright+maginsHeight; 238 | if(itemOffsetcontrolHeight/2){ 243 | double offHeight=itemOffset-controlHeight/2+smallTextHright; 244 | radian=-asin(offHeight/radius); 245 | }else if(itemOffset==controlHeight/2){ 246 | radian=0.0; 247 | } 248 | print(radian); 249 | return radian; 250 | } 251 | 252 | void drawText(Canvas canvas,TextPainter painterSmall,double itemOffset,String texts){ 253 | canvas.save(); 254 | canvas.translate(0.0, itemOffset); 255 | painterSmall.text=new TextSpan(text: texts,style: new TextStyle(fontSize: 18.0,color: Colors.black)); 256 | painterSmall.layout(); 257 | if(smallTextHright==0.0){ 258 | smallTextHright=painterSmall.size.height; 259 | } 260 | double smallTextWidth=painterSmall.size.width; 261 | //计算间距 262 | if(maginsHeight==0.0){ 263 | maginsHeight=(130.0-smallTextHright*5.0)/4; 264 | } 265 | try { 266 | canvas.transform(Matrix4.rotationX(getRadian(itemOffset, 130.0, maginsHeight, smallTextHright)).storage); 267 | }catch(e){ 268 | 269 | } 270 | painterSmall.paint(canvas, new Offset((80-smallTextWidth)/2, 0.0)); 271 | canvas.restore(); 272 | } 273 | 274 | 275 | 276 | @override 277 | void paint(Canvas canvas, Size size) { 278 | parseOffset(); 279 | canvas.clipRect(new Rect.fromLTRB(0.0, 0.0, 80.0, 130.0)); 280 | 281 | TextPainter painterSmall=new TextPainter(); 282 | painterSmall.textDirection=TextDirection.ltr; 283 | painterSmall.maxLines=1; 284 | 285 | //第一条Item,初始化不可见 286 | drawText(canvas,painterSmall,-maginsHeight-smallTextHright+offset,hours[0]); 287 | //第二条Item 288 | drawText(canvas,painterSmall,offset,hours[1]); 289 | //第三条Item 290 | drawText(canvas,painterSmall,maginsHeight+smallTextHright+offset,hours[2]); 291 | //最中间的Item 292 | drawText(canvas,painterSmall,maginsHeight*2+smallTextHright*2+offset,hours[3]); 293 | //第五条Item 294 | drawText(canvas,painterSmall,maginsHeight*3+smallTextHright*3+offset,hours[4]); 295 | //第六条Item 296 | drawText(canvas,painterSmall,maginsHeight*4+smallTextHright*4+offset,hours[5]); 297 | //第七条Item 298 | drawText(canvas,painterSmall,maginsHeight*5+smallTextHright*5+offset,hours[6]); 299 | 300 | Rect rect=Rect.fromLTRB(0.0, 0.0, 80.0, maginsHeight*2+smallTextHright*2); 301 | Paint paintRext=new Paint(); 302 | paintRext.color=Color(0x90ffffff); 303 | canvas.drawRect(rect, paintRext); 304 | 305 | canvas.translate(0.0, maginsHeight*2+smallTextHright*3); 306 | rect=Rect.fromLTRB(0.0, 0.0, 80.0, maginsHeight*2+smallTextHright*2); 307 | canvas.drawRect(rect, paintRext); 308 | } 309 | 310 | @override 311 | bool shouldRepaint(CustomPainter oldDelegate) { 312 | return true; 313 | } 314 | 315 | } 316 | 317 | -------------------------------------------------------------------------------- /lib/ui/thirdpage/cutScreen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'dart:ui' as ui show Image; 3 | 4 | import 'package:flutter/rendering.dart'; 5 | 6 | class CutScreen extends StatefulWidget{ 7 | 8 | @override 9 | State createState() { 10 | return new CutScreenState(); 11 | } 12 | 13 | } 14 | 15 | class CutScreenState extends State{ 16 | 17 | ui.Image image; 18 | ImagePaint paintCanvas; 19 | GlobalKey _globalKey=new GlobalKey(); 20 | double screenWidth,screenHeight; 21 | 22 | 23 | @override 24 | void didChangeDependencies() { 25 | super.didChangeDependencies(); 26 | Size size=MediaQuery.of(context).size; 27 | screenWidth=size.width; 28 | screenHeight=size.height; 29 | } 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | return new RepaintBoundary( 34 | key: _globalKey, 35 | child: new Scaffold( 36 | appBar: new AppBar(title: new Text("界面3"),), 37 | body:new Column( 38 | children: [ 39 | new RaisedButton( 40 | child:new Text("点击显示图片"), 41 | onPressed: () async { 42 | RenderRepaintBoundary boundary = _globalKey.currentContext 43 | .findRenderObject(); 44 | image = await boundary.toImage(pixelRatio: 1.0); 45 | setState(() { 46 | 47 | }); 48 | } 49 | ), 50 | new CustomPaint( 51 | size: new Size(screenWidth, screenHeight), 52 | painter: paintCanvas=new ImagePaint(image,screenWidth,screenHeight), 53 | ), 54 | ], 55 | ), 56 | ), 57 | ); 58 | } 59 | } 60 | 61 | class ImagePaint extends CustomPainter{ 62 | 63 | ImagePaint(this.image,this.screenWidth,this.screenHeight); 64 | 65 | ui.Image image; 66 | final double screenWidth,screenHeight; 67 | 68 | @override 69 | void paint(Canvas canvas, Size size) { 70 | if(image!=null){ 71 | canvas.drawImage(image, new Offset(0.0, 0.0), new Paint()); 72 | }else{ 73 | Paint paint=new Paint(); 74 | paint.color=Colors.amberAccent; 75 | canvas.drawRect(new Rect.fromLTRB(0.0, 0.0, screenWidth, screenHeight), paint); 76 | } 77 | } 78 | 79 | @override 80 | bool shouldRepaint(CustomPainter oldDelegate) { 81 | return true; 82 | } 83 | 84 | } -------------------------------------------------------------------------------- /lib/ui/thirdpage/examples/CameraTest.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'dart:async'; 3 | import 'dart:io'; 4 | import 'package:camera/camera.dart'; 5 | import 'package:path_provider/path_provider.dart'; 6 | import 'package:flutterapp/main.dart'; 7 | 8 | 9 | 10 | class CameraExampleHome extends StatefulWidget { 11 | 12 | @override 13 | _CameraExampleHomeState createState() { 14 | return _CameraExampleHomeState(); 15 | } 16 | } 17 | 18 | void logError(String code, String message) => 19 | print('Error: $code\nError Message: $message'); 20 | 21 | class _CameraExampleHomeState extends State { 22 | CameraController controller; 23 | String imagePath; 24 | String videoPath; 25 | VoidCallback videoPlayerListener; 26 | 27 | final GlobalKey _scaffoldKey = GlobalKey(); 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Scaffold( 32 | key: _scaffoldKey, 33 | appBar: AppBar( 34 | title: const Text('Camera example'), 35 | ), 36 | body: Column( 37 | children: [ 38 | Expanded( 39 | child: Container( 40 | child: Padding( 41 | padding: const EdgeInsets.all(1.0), 42 | child: Center( 43 | child: _cameraPreviewWidget(), 44 | ), 45 | ), 46 | decoration: BoxDecoration( 47 | color: Colors.black, 48 | border: Border.all( 49 | color: controller != null && controller.value.isRecordingVideo 50 | ? Colors.redAccent 51 | : Colors.grey, 52 | width: 3.0, 53 | ), 54 | ), 55 | ), 56 | ), 57 | Padding( 58 | padding: const EdgeInsets.all(5.0), 59 | child: Row( 60 | mainAxisAlignment: MainAxisAlignment.start, 61 | children: [ 62 | _captureControlRowWidget(), 63 | _thumbnailWidget(), 64 | ], 65 | ), 66 | ), 67 | ], 68 | ), 69 | ); 70 | } 71 | 72 | /// 显示相机的预览(如果预览不可用,则显示消息)。 73 | /// Display the preview from the camera (or a message if the preview is not available). 74 | Widget _cameraPreviewWidget() { 75 | if (controller == null || !controller.value.isInitialized) { 76 | return const Text( 77 | 'Tap a camera', 78 | style: TextStyle( 79 | color: Colors.white, 80 | fontSize: 24.0, 81 | fontWeight: FontWeight.w900, 82 | ), 83 | ); 84 | } else { 85 | return AspectRatio( 86 | aspectRatio: controller.value.aspectRatio, 87 | child: CameraPreview(controller), 88 | ); 89 | } 90 | } 91 | 92 | /// 显示捕获的图像或视频的缩略图。 93 | /// Display the thumbnail of the captured image or video. 94 | Widget _thumbnailWidget() { 95 | return Expanded( 96 | child: Align( 97 | alignment: Alignment.centerRight, 98 | child: imagePath == null 99 | ? null : SizedBox( 100 | child: Image.file(File(imagePath)), 101 | width: 64.0, 102 | height: 64.0, 103 | ), 104 | ), 105 | ); 106 | } 107 | 108 | /// 使用按钮显示控制栏以拍摄照片和录制视频。 109 | /// Display the control bar with buttons to take pictures and record videos. 110 | Widget _captureControlRowWidget() { 111 | return Row( 112 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 113 | mainAxisSize: MainAxisSize.max, 114 | children: [ 115 | IconButton( 116 | icon: const Icon(Icons.camera_alt), 117 | color: Colors.blue, 118 | onPressed: controller != null && 119 | controller.value.isInitialized && 120 | !controller.value.isRecordingVideo 121 | ? onTakePictureButtonPressed 122 | : null, 123 | ), 124 | 125 | IconButton( 126 | //cameraDescription 127 | icon: cameraChangeIcon, 128 | onPressed: (){onNewCameraSelected();}, 129 | ), 130 | ], 131 | ); 132 | } 133 | 134 | 135 | Icon cameraChangeIcon = Icon(Icons.camera_rear); 136 | String timestamp() => DateTime.now().millisecondsSinceEpoch.toString(); 137 | 138 | 139 | void showInSnackBar(String message) { 140 | _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text(message))); 141 | } 142 | 143 | 144 | void onNewCameraSelected() async { 145 | if(controller != null && controller.value.isRecordingVideo){ 146 | return; 147 | } 148 | if(controller!=null){ 149 | for (CameraDescription cameraDescription in cameras) { 150 | if(controller.description == cameraDescription){ 151 | continue; 152 | }else{ 153 | if(cameraDescription.lensDirection == CameraLensDirection.back||cameraDescription.lensDirection == CameraLensDirection.front){ 154 | if (controller != null) { 155 | await controller.dispose(); 156 | } 157 | controller = CameraController(cameraDescription, ResolutionPreset.high); 158 | break; 159 | } 160 | } 161 | } 162 | }else{ 163 | for (CameraDescription cameraDescription in cameras) { 164 | if(cameraDescription.lensDirection == CameraLensDirection.back){ 165 | controller = CameraController(cameraDescription, ResolutionPreset.high); 166 | break; 167 | } 168 | } 169 | } 170 | // If the controller is updated then update the UI. 171 | controller.addListener(() { 172 | if (mounted) setState(() {}); 173 | if (controller.value.hasError) { 174 | showInSnackBar('Camera error ${controller.value.errorDescription}'); 175 | } 176 | }); 177 | 178 | try { 179 | await controller.initialize(); 180 | } on CameraException catch (e) { 181 | _showCameraException(e); 182 | } 183 | 184 | if (mounted) { 185 | setState(() { 186 | cameraChangeIcon = controller.description.lensDirection 187 | == CameraLensDirection.back ? Icon(Icons.camera_rear) : Icon(Icons.camera_front); 188 | }); 189 | } 190 | } 191 | 192 | void onTakePictureButtonPressed() { 193 | takePicture().then((String filePath) { 194 | if (mounted) { 195 | setState(() { 196 | imagePath = filePath; 197 | }); 198 | if (filePath != null) showInSnackBar('Picture saved to $filePath'); 199 | } 200 | }); 201 | } 202 | 203 | 204 | Future takePicture() async { 205 | if (!controller.value.isInitialized) { 206 | showInSnackBar('Error: select a camera first.'); 207 | return null; 208 | } 209 | final Directory extDir = await getApplicationDocumentsDirectory(); 210 | final String dirPath = '${extDir.path}/Pictures/flutter_test'; 211 | await Directory(dirPath).create(recursive: true); 212 | final String filePath = '$dirPath/${timestamp()}.jpg'; 213 | 214 | if (controller.value.isTakingPicture) { 215 | // A capture is already pending, do nothing. 216 | return null; 217 | } 218 | 219 | try { 220 | await controller.takePicture(filePath); 221 | } on CameraException catch (e) { 222 | _showCameraException(e); 223 | return null; 224 | } 225 | return filePath; 226 | } 227 | 228 | void _showCameraException(CameraException e) { 229 | logError(e.code, e.description); 230 | showInSnackBar('Error: ${e.code}\n${e.description}'); 231 | } 232 | } 233 | 234 | -------------------------------------------------------------------------------- /lib/ui/thirdpage/examples/Examples.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutterapp/ui/thirdpage/examples/CameraTest.dart'; 3 | import 'package:flutterapp/ui/thirdpage/examples/ImagePicker.dart'; 4 | 5 | class Examples extends StatelessWidget{ 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return new Scaffold( 10 | appBar: new AppBar( 11 | title: new Text("Examples"), 12 | ), 13 | body: new Container( 14 | margin: EdgeInsets.all(10), 15 | child: new Wrap( 16 | spacing: 10, 17 | children: [ 18 | new GestureDetector( 19 | child: new Chip(label: new Text("Camera")), 20 | onTap: (){ 21 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) { 22 | return new CameraExampleHome(); 23 | })); 24 | }, 25 | ), 26 | 27 | new GestureDetector( 28 | child: new Chip(label: new Text("ImagePicker")), 29 | onTap: (){ 30 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) { 31 | return new ImagePickerApp(); 32 | })); 33 | }, 34 | ), 35 | ], 36 | ), 37 | ), 38 | ); 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /lib/ui/thirdpage/examples/ImagePicker.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:image_picker/image_picker.dart'; 6 | import 'package:video_player/video_player.dart'; 7 | 8 | class ImagePickerApp extends StatefulWidget { 9 | ImagePickerApp({Key key, this.title}) : super(key: key); 10 | 11 | final String title; 12 | 13 | @override 14 | _MyHomePageState createState() => _MyHomePageState(); 15 | } 16 | 17 | class _MyHomePageState extends State { 18 | Future _imageFile; 19 | bool isVideo = false; 20 | VideoPlayerController _controller; 21 | VoidCallback listener; 22 | 23 | void _onImageButtonPressed(ImageSource source) { 24 | setState(() { 25 | if (_controller != null) { 26 | _controller.setVolume(0.0); 27 | _controller.removeListener(listener); 28 | } 29 | if (isVideo) { 30 | ImagePicker.pickVideo(source: source).then((File file) { 31 | if (file != null && mounted) { 32 | setState(() { 33 | _controller = VideoPlayerController.file(file) 34 | ..addListener(listener) 35 | ..setVolume(1.0) 36 | ..initialize() 37 | ..setLooping(true) 38 | ..play(); 39 | }); 40 | } 41 | }); 42 | } else { 43 | _imageFile = ImagePicker.pickImage(source: source); 44 | } 45 | }); 46 | } 47 | 48 | @override 49 | void deactivate() { 50 | if (_controller != null) { 51 | _controller.setVolume(0.0); 52 | _controller.removeListener(listener); 53 | } 54 | super.deactivate(); 55 | } 56 | 57 | @override 58 | void dispose() { 59 | if (_controller != null) { 60 | _controller.dispose(); 61 | } 62 | super.dispose(); 63 | } 64 | 65 | @override 66 | void initState() { 67 | super.initState(); 68 | listener = () { 69 | setState(() {}); 70 | }; 71 | } 72 | 73 | Widget _previewVideo(VideoPlayerController controller) { 74 | if (controller == null) { 75 | return const Text( 76 | 'You have not yet picked a video', 77 | textAlign: TextAlign.center, 78 | ); 79 | } else if (controller.value.initialized) { 80 | return Padding( 81 | padding: const EdgeInsets.all(10.0), 82 | child: AspectRatioVideo(controller), 83 | ); 84 | } else { 85 | return const Text( 86 | 'Error Loading Video', 87 | textAlign: TextAlign.center, 88 | ); 89 | } 90 | } 91 | 92 | Widget _previewImage() { 93 | return FutureBuilder( 94 | future: _imageFile, 95 | builder: (BuildContext context, AsyncSnapshot snapshot) { 96 | if (snapshot.connectionState == ConnectionState.done && 97 | snapshot.data != null) { 98 | return Image.file(snapshot.data); 99 | } else if (snapshot.error != null) { 100 | return const Text( 101 | 'Error picking image.', 102 | textAlign: TextAlign.center, 103 | ); 104 | } else { 105 | return const Text( 106 | 'You have not yet picked an image.', 107 | textAlign: TextAlign.center, 108 | ); 109 | } 110 | }); 111 | } 112 | 113 | @override 114 | Widget build(BuildContext context) { 115 | return Scaffold( 116 | appBar: AppBar( 117 | title: Text("ImagePicker"), 118 | ), 119 | body: Center( 120 | child: isVideo ? _previewVideo(_controller) : _previewImage(), 121 | ), 122 | floatingActionButton: Column( 123 | mainAxisAlignment: MainAxisAlignment.end, 124 | children: [ 125 | FloatingActionButton( 126 | onPressed: () { 127 | isVideo = false; 128 | _onImageButtonPressed(ImageSource.gallery); 129 | }, 130 | heroTag: 'image0', 131 | tooltip: 'Pick Image from gallery', 132 | child: const Icon(Icons.photo_library), 133 | ), 134 | Padding( 135 | padding: const EdgeInsets.only(top: 16.0), 136 | child: FloatingActionButton( 137 | onPressed: () { 138 | isVideo = false; 139 | _onImageButtonPressed(ImageSource.camera); 140 | }, 141 | heroTag: 'image1', 142 | tooltip: 'Take a Photo', 143 | child: const Icon(Icons.camera_alt), 144 | ), 145 | ), 146 | Padding( 147 | padding: const EdgeInsets.only(top: 16.0), 148 | child: FloatingActionButton( 149 | backgroundColor: Colors.red, 150 | onPressed: () { 151 | isVideo = true; 152 | _onImageButtonPressed(ImageSource.gallery); 153 | }, 154 | heroTag: 'video0', 155 | tooltip: 'Pick Video from gallery', 156 | child: const Icon(Icons.video_library), 157 | ), 158 | ), 159 | Padding( 160 | padding: const EdgeInsets.only(top: 16.0), 161 | child: FloatingActionButton( 162 | backgroundColor: Colors.red, 163 | onPressed: () { 164 | isVideo = true; 165 | _onImageButtonPressed(ImageSource.camera); 166 | }, 167 | heroTag: 'video1', 168 | tooltip: 'Take a Video', 169 | child: const Icon(Icons.videocam), 170 | ), 171 | ), 172 | ], 173 | ), 174 | ); 175 | } 176 | } 177 | 178 | class AspectRatioVideo extends StatefulWidget { 179 | AspectRatioVideo(this.controller); 180 | 181 | final VideoPlayerController controller; 182 | 183 | @override 184 | AspectRatioVideoState createState() => AspectRatioVideoState(); 185 | } 186 | 187 | class AspectRatioVideoState extends State { 188 | VideoPlayerController get controller => widget.controller; 189 | bool initialized = false; 190 | 191 | VoidCallback listener; 192 | 193 | @override 194 | void initState() { 195 | super.initState(); 196 | listener = () { 197 | if (!mounted) { 198 | return; 199 | } 200 | if (initialized != controller.value.initialized) { 201 | initialized = controller.value.initialized; 202 | setState(() {}); 203 | } 204 | }; 205 | controller.addListener(listener); 206 | } 207 | 208 | @override 209 | Widget build(BuildContext context) { 210 | if (initialized) { 211 | return Center( 212 | child: AspectRatio( 213 | aspectRatio: controller.value?.aspectRatio, 214 | child: VideoPlayer(controller), 215 | ), 216 | ); 217 | } else { 218 | return Container(); 219 | } 220 | } 221 | } 222 | 223 | 224 | -------------------------------------------------------------------------------- /lib/ui/thirdpage/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | typedef OnPageChange(int index); 5 | 6 | class HomePage extends StatefulWidget { 7 | @override 8 | _HomePageState createState() => _HomePageState(); 9 | } 10 | 11 | 12 | class _HomePageState extends State { 13 | var _currentIndex = 0; 14 | PageController _pageController; 15 | final List images = [ 16 | 'images/chaonan1.jpeg', 17 | 'images/chaonan2.jpg', 18 | 'images/chaonan3.jpeg', 19 | ]; 20 | 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | num screenWidth = MediaQuery.of(context).size.width; 25 | return Scaffold( 26 | appBar: AppBar( 27 | title: Text('mts主页'), 28 | iconTheme: IconThemeData(color: Colors.blueAccent), 29 | ), 30 | body: SizedBox( 31 | height: screenWidth * 0.8 / 4 * 3, 32 | child: PageView.builder( 33 | controller: _pageController, 34 | itemCount: images.length, 35 | onPageChanged: (index) { 36 | setState(() { 37 | _currentIndex = index; 38 | }); 39 | }, 40 | itemBuilder: (BuildContext context, int index) { 41 | return PageChild( 42 | index: index, 43 | imgPath: images[index], 44 | onPageChange: (int index){ 45 | setState(() { 46 | _currentIndex = index; 47 | }); 48 | }, 49 | imgLength: images.length, 50 | pageController: _pageController, 51 | ); 52 | }), 53 | )); 54 | } 55 | 56 | @override 57 | void initState() { 58 | super.initState(); 59 | _pageController = 60 | PageController(initialPage: images.length % 2, viewportFraction: 0.8); 61 | } 62 | } 63 | 64 | class PageChild extends StatefulWidget{ 65 | 66 | final int index; 67 | final String imgPath; 68 | final OnPageChange onPageChange; 69 | final PageController pageController; 70 | final int imgLength; 71 | 72 | PageChild({ 73 | @required this.index, 74 | @required this.imgPath, 75 | @required this.onPageChange, 76 | @required this.pageController, 77 | @required this.imgLength, 78 | }); 79 | 80 | @override 81 | State createState() { 82 | return PageChildState(); 83 | } 84 | } 85 | 86 | class PageChildState extends State{ 87 | @override 88 | Widget build(BuildContext context) { 89 | return InkWell( 90 | onTap: () { 91 | print(widget.index); 92 | widget.pageController.animateToPage(widget.index % widget.imgLength, 93 | duration: Duration(milliseconds: 500), 94 | curve: Curves.fastOutSlowIn); 95 | }, 96 | child: Padding( 97 | padding: const EdgeInsets.all(10.0), 98 | child: ClipRRect( 99 | borderRadius: BorderRadius.circular(16.0), 100 | child: Image.asset( 101 | widget.imgPath, 102 | fit: BoxFit.cover, 103 | ), 104 | ), 105 | ), 106 | ); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /lib/ui/thirdpage/shakeView.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class ShakeView extends StatefulWidget{ 6 | @override 7 | State createState() { 8 | return new ShakeViewState(); 9 | } 10 | } 11 | 12 | class ShakeViewState extends State with SingleTickerProviderStateMixin{ 13 | 14 | Animation animation; 15 | AnimationController animationController; 16 | double rotateAngle=0.0; 17 | Random _random=new Random(); 18 | double doubleAngle ; 19 | var listInfo =[-0.05,0.03,0.04,-0.02,0.05,-0.04,0.02,0.05,-0.03,0.04,0.03,-0.06,0.02,-0.05,0.03,0.04,-0.02,0.05,-0.04,0.02,0.05,-0.03]; 20 | var listAngles =[0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0]; 21 | int count = 0; 22 | bool isTiming = false; 23 | 24 | @override 25 | void initState() { 26 | super.initState(); 27 | animationController = new AnimationController(duration: const Duration(milliseconds: 300), vsync: this,animationBehavior: AnimationBehavior.preserve); 28 | 29 | animation = new Tween(begin: 0.0, end: 2.0).animate(animationController) 30 | ..addListener(() { 31 | double animationValue = animation.value; 32 | setState(() { 33 | for(int i =0 ;i< listInfo.length;i++){ 34 | doubleAngle = listInfo[i] *2; 35 | if(animationValue<=0.5){ 36 | listAngles[i] = animationValue * doubleAngle; 37 | }else if(animationValue<=1.0){ 38 | listAngles[i] = (1-animationValue) * doubleAngle; 39 | }else if(animationValue<=1.5){ 40 | listAngles[i] = -(animationValue-1.0) * doubleAngle; 41 | }else{ 42 | listAngles[i] = -(2.0-animationValue) * doubleAngle; 43 | } 44 | } 45 | }); 46 | if(animationValue==2.0){ 47 | changeData(); 48 | } 49 | }); 50 | } 51 | 52 | void changeData()async{ 53 | print("changeData"); 54 | if(count%3 == 0){ 55 | int d; 56 | double c; 57 | for(int i =0 ;i< listInfo.length;i++){ 58 | d = _random.nextInt(10); 59 | if(d>5){ 60 | c = listInfo[i] = listInfo[i]+0.01; 61 | }else{ 62 | c = listInfo[i] = listInfo[i]-0.01; 63 | } 64 | 65 | if(c<0.02 && c >=0){ 66 | listInfo[i] = 0.03; 67 | }else if(c>-0.02 && c <=0){ 68 | listInfo[i] = -0.03; 69 | }else if(c<-0.07){ 70 | listInfo[i] = -0.06; 71 | }else if(c>0.07){ 72 | listInfo[i] = 0.06; 73 | } 74 | } 75 | } 76 | count++; 77 | } 78 | 79 | 80 | 81 | 82 | void startTiming() async{ 83 | while(isTiming) { 84 | await Future.delayed(Duration(milliseconds: 300 * 3)); 85 | if(isTiming){ 86 | changeData(); 87 | } 88 | } 89 | } 90 | 91 | 92 | void _onLongPress(){ 93 | if(animationController.isAnimating){ 94 | return; 95 | } 96 | isTiming=true; 97 | startTiming(); 98 | animationController.repeat(); 99 | } 100 | 101 | @override 102 | Widget build(BuildContext context) { 103 | return new Scaffold( 104 | appBar: new AppBar( 105 | title: new Text("ShakeView"), 106 | actions: [ 107 | new GestureDetector( 108 | onTap: (){ 109 | if(animationController.isAnimating){ 110 | isTiming = false; 111 | animationController.stop(); 112 | } 113 | setState(() { 114 | listAngles =[0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0]; 115 | }); 116 | }, 117 | child: new Container( 118 | padding: EdgeInsets.all(5), 119 | child: new Text("完成"), 120 | ), 121 | ), 122 | ], 123 | ), 124 | body: new Column( 125 | children: [ 126 | getRows(0), 127 | getRows(4), 128 | getRows(8), 129 | getRows(12), 130 | getRows(16), 131 | getRowsLess(), 132 | ], 133 | ), 134 | ); 135 | } 136 | 137 | 138 | Widget getRows(int miniPosition){ 139 | return new Row( 140 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 141 | children: [ 142 | getItem(miniPosition), 143 | getItem(miniPosition+1), 144 | getItem(miniPosition+2), 145 | getItem(miniPosition+3), 146 | ], 147 | ); 148 | } 149 | 150 | Widget getRowsLess(){ 151 | return new Row( 152 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 153 | children: [ 154 | getItem(20), 155 | getItem(21), 156 | new Container( 157 | margin: EdgeInsets.all(10), 158 | height: 60, 159 | width: 60, 160 | ), 161 | new Container( 162 | margin: EdgeInsets.all(10), 163 | height: 60, 164 | width: 60, 165 | ), 166 | ], 167 | ); 168 | } 169 | 170 | 171 | Widget getItem(int position){ 172 | return new GestureDetector( 173 | onLongPress: (){ 174 | _onLongPress(); 175 | }, 176 | child:Transform.rotate( 177 | origin: new Offset(0, 0), 178 | angle: listAngles[position], 179 | child: new Container( 180 | margin: EdgeInsets.all(10), 181 | height: 60, 182 | width: 60, 183 | decoration: BoxDecoration( 184 | color: Colors.black45, 185 | borderRadius: BorderRadius.all(Radius.circular(5)), 186 | ), 187 | ), 188 | ), 189 | ); 190 | } 191 | 192 | @override 193 | void dispose() { 194 | isTiming = false; 195 | animationController.dispose(); 196 | super.dispose(); 197 | } 198 | } -------------------------------------------------------------------------------- /lib/ui/thirdpage/testFile.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class TestFile extends StatefulWidget{ 4 | @override 5 | State createState() { 6 | return new TestFileState(); 7 | } 8 | } 9 | 10 | class TestFileState extends State{ 11 | 12 | var texts = ["123","234","345","456","789","101","102","103","104","end"]; 13 | String txt = "START"; 14 | bool isStart = true; 15 | int count = 0; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return new Scaffold( 20 | appBar: new AppBar( 21 | title: new Text("TestFile"), 22 | ), 23 | body: new Column( 24 | children: [ 25 | new GestureDetector( 26 | child: new Text(txt), 27 | onTap: () async { 28 | while(isStart){ 29 | setState(() { 30 | txt = texts[count]; 31 | }); 32 | count++; 33 | if(count>=texts.length){ 34 | count=0; 35 | } 36 | await Future.delayed(new Duration(milliseconds: 1000)); 37 | } 38 | }, 39 | ), 40 | new MyStateLessWidget(), 41 | ], 42 | ), 43 | ); 44 | } 45 | 46 | @override 47 | void dispose() { 48 | isStart=false; 49 | super.dispose(); 50 | } 51 | } 52 | 53 | 54 | class MyStateLessWidget extends StatefulWidget{ 55 | @override 56 | State createState() { 57 | return MyStateLessWidgetState(); 58 | } 59 | 60 | } 61 | 62 | 63 | class MyStateLessWidgetState extends State{ 64 | @override 65 | Widget build(BuildContext context) { 66 | print("MyStateLessWidget is invokell"); 67 | return new Text("I am a StatelessWidget",style: new TextStyle(fontSize: 18,color: Colors.red),); 68 | } 69 | } -------------------------------------------------------------------------------- /lib/ui/thirdpage/thirdPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:flutter/rendering.dart'; 4 | import 'package:flutterapp/bin/appinfobin.dart'; 5 | import 'package:flutterapp/ui/thirdpage/shakeView.dart'; 6 | import 'package:flutterapp/ui/thirdpage/testFile.dart'; 7 | import 'package:flutterapp/ui/thirdpage/examples/Examples.dart'; 8 | import 'package:flutterapp/ui/thirdpage/cutScreen.dart'; 9 | import 'package:flutterapp/ui/thirdpage/home_page.dart'; 10 | 11 | class ThirdPage extends StatefulWidget{ 12 | 13 | @override 14 | State createState() { 15 | return new ThirdPageState(); 16 | } 17 | 18 | } 19 | 20 | class ThirdPageState extends State{ 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return new Scaffold( 25 | appBar: new AppBar( 26 | title: new Text("CustomWidght",style: new TextStyle(fontSize: 18.0),), 27 | actions: [ 28 | new IconButton( 29 | icon: new Icon(Icons.search), 30 | onPressed: null, 31 | iconSize: 29.0, 32 | disabledColor: Colors.white,) 33 | ] 34 | ,), 35 | body: new Container( 36 | // ignore: conflicting_dart_import 37 | child: new ListViewWidgets(), 38 | ) 39 | ); 40 | } 41 | } 42 | 43 | 44 | class ListViewWidgets extends StatefulWidget{ 45 | 46 | final constantList = [ 47 | new AppInfoBin("CutScreen", "2019-01-11 10:25", "屏幕截图",false), 48 | new AppInfoBin("Examples", "2019-04-17 11:16", "各种示例",false), 49 | new AppInfoBin("ShakeView", "2019-04-18 10:06", "可以摇晃的View",false), 50 | new AppInfoBin("TEST", "2019-04-20 15:17", "测试类",false), 51 | new AppInfoBin("Gallery", "2019-05-27 11:03", "画廊",false), 52 | ]; 53 | 54 | @override 55 | State createState() { 56 | return new ListState(); 57 | } 58 | 59 | } 60 | 61 | class ListState extends State{ 62 | 63 | @override 64 | Widget build(BuildContext context) { 65 | return new ListView.builder( 66 | itemCount: widget.constantList.length, 67 | itemBuilder: (BuildContext context,int index){ 68 | return new GestureDetector( 69 | onTapDown: (TapDownDetails details){ 70 | setState(() { 71 | widget.constantList[index].isTapDown=true; 72 | }); 73 | }, 74 | onTapCancel: (){ 75 | setState(() { 76 | widget.constantList[index].isTapDown=false; 77 | }); 78 | }, 79 | onTapUp:(TapUpDetails details){ 80 | setState(() { 81 | widget.constantList[index].isTapDown=false; 82 | }); 83 | if(index==0){ 84 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) { 85 | return new CutScreen(); 86 | })); 87 | }else if(index==1){ 88 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) { 89 | return new Examples(); 90 | })); 91 | }else if(index==2){ 92 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) { 93 | return new ShakeView(); 94 | })); 95 | }else if(index==3){ 96 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) { 97 | return new TestFile(); 98 | })); 99 | }else if(index==4){ 100 | Navigator.of(context).push(new MaterialPageRoute(builder: (_) { 101 | return new HomePage(); 102 | })); 103 | } 104 | }, 105 | child:new Card( 106 | color: widget.constantList[index].isTapDown?Color(0xFFF4CB28):null, 107 | child: new Container( 108 | padding: new EdgeInsets.all(10.0), 109 | child: new ListTile( 110 | subtitle: new Container( 111 | child: new Column( 112 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 113 | children: [ 114 | new Row( 115 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 116 | children: [ 117 | new Text(widget.constantList[index].title,style: new TextStyle(fontWeight: FontWeight.bold,fontSize: 16.0,color: Colors.black),) 118 | ], 119 | ), 120 | new Row( 121 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 122 | children: [ 123 | new Row( 124 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 125 | crossAxisAlignment: CrossAxisAlignment.center, 126 | children: [ 127 | new Text("时间",style: new TextStyle(fontSize: 13.0)), 128 | new Text(widget.constantList[index].times,style: new TextStyle(fontSize: 13.0)), 129 | ], 130 | ) 131 | ], 132 | ), 133 | new Row( 134 | children: [ 135 | new Expanded( 136 | child:new Container( 137 | padding: const EdgeInsets.fromLTRB(0.0, 8.0, 0.0, 2.0), 138 | child: new Text(widget.constantList[index].content,maxLines: 1,overflow: TextOverflow.ellipsis,), 139 | ), 140 | ) 141 | ], 142 | ) 143 | ], 144 | ), 145 | ), 146 | trailing: new Icon(Icons.keyboard_arrow_right,color: Colors.grey,), 147 | ), 148 | ), 149 | ) 150 | ); 151 | },); 152 | } 153 | } -------------------------------------------------------------------------------- /pictures/Dragable gridview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFlutter/PullToRefresh/fe11bedb786528987b14af7f82db61fb7985582a/pictures/Dragable gridview.png -------------------------------------------------------------------------------- /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.flutter-io.cn" 9 | source: hosted 10 | version: "2.1.0" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.flutter-io.cn" 16 | source: hosted 17 | version: "1.0.4" 18 | camera: 19 | dependency: "direct main" 20 | description: 21 | name: camera 22 | url: "https://pub.flutter-io.cn" 23 | source: hosted 24 | version: "0.4.3+2" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.flutter-io.cn" 30 | source: hosted 31 | version: "1.1.2" 32 | collection: 33 | dependency: transitive 34 | description: 35 | name: collection 36 | url: "https://pub.flutter-io.cn" 37 | source: hosted 38 | version: "1.14.11" 39 | cupertino_icons: 40 | dependency: "direct main" 41 | description: 42 | name: cupertino_icons 43 | url: "https://pub.flutter-io.cn" 44 | source: hosted 45 | version: "0.1.2" 46 | dragablegridview_flutter: 47 | dependency: "direct main" 48 | description: 49 | name: dragablegridview_flutter 50 | url: "https://pub.flutter-io.cn" 51 | source: hosted 52 | version: "0.0.4" 53 | english_words: 54 | dependency: "direct main" 55 | description: 56 | name: english_words 57 | url: "https://pub.flutter-io.cn" 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_localizations: 66 | dependency: "direct main" 67 | description: flutter 68 | source: sdk 69 | version: "0.0.0" 70 | flutter_test: 71 | dependency: "direct dev" 72 | description: flutter 73 | source: sdk 74 | version: "0.0.0" 75 | image_picker: 76 | dependency: "direct main" 77 | description: 78 | name: image_picker 79 | url: "https://pub.flutter-io.cn" 80 | source: hosted 81 | version: "0.5.4+3" 82 | intl: 83 | dependency: transitive 84 | description: 85 | name: intl 86 | url: "https://pub.flutter-io.cn" 87 | source: hosted 88 | version: "0.15.8" 89 | marquee_flutter: 90 | dependency: "direct main" 91 | description: 92 | name: marquee_flutter 93 | url: "https://pub.flutter-io.cn" 94 | source: hosted 95 | version: "0.0.2" 96 | matcher: 97 | dependency: transitive 98 | description: 99 | name: matcher 100 | url: "https://pub.flutter-io.cn" 101 | source: hosted 102 | version: "0.12.5" 103 | meta: 104 | dependency: transitive 105 | description: 106 | name: meta 107 | url: "https://pub.flutter-io.cn" 108 | source: hosted 109 | version: "1.1.6" 110 | path: 111 | dependency: transitive 112 | description: 113 | name: path 114 | url: "https://pub.flutter-io.cn" 115 | source: hosted 116 | version: "1.6.2" 117 | path_provider: 118 | dependency: "direct main" 119 | description: 120 | name: path_provider 121 | url: "https://pub.flutter-io.cn" 122 | source: hosted 123 | version: "0.5.0+1" 124 | pedantic: 125 | dependency: transitive 126 | description: 127 | name: pedantic 128 | url: "https://pub.flutter-io.cn" 129 | source: hosted 130 | version: "1.5.0" 131 | pulltorefresh_flutter: 132 | dependency: "direct main" 133 | description: 134 | name: pulltorefresh_flutter 135 | url: "https://pub.flutter-io.cn" 136 | source: hosted 137 | version: "0.1.9" 138 | quiver: 139 | dependency: transitive 140 | description: 141 | name: quiver 142 | url: "https://pub.flutter-io.cn" 143 | source: hosted 144 | version: "2.0.2" 145 | sky_engine: 146 | dependency: transitive 147 | description: flutter 148 | source: sdk 149 | version: "0.0.99" 150 | source_span: 151 | dependency: transitive 152 | description: 153 | name: source_span 154 | url: "https://pub.flutter-io.cn" 155 | source: hosted 156 | version: "1.5.5" 157 | stack_trace: 158 | dependency: transitive 159 | description: 160 | name: stack_trace 161 | url: "https://pub.flutter-io.cn" 162 | source: hosted 163 | version: "1.9.3" 164 | stream_channel: 165 | dependency: transitive 166 | description: 167 | name: stream_channel 168 | url: "https://pub.flutter-io.cn" 169 | source: hosted 170 | version: "2.0.0" 171 | string_scanner: 172 | dependency: transitive 173 | description: 174 | name: string_scanner 175 | url: "https://pub.flutter-io.cn" 176 | source: hosted 177 | version: "1.0.4" 178 | term_glyph: 179 | dependency: transitive 180 | description: 181 | name: term_glyph 182 | url: "https://pub.flutter-io.cn" 183 | source: hosted 184 | version: "1.1.0" 185 | test_api: 186 | dependency: transitive 187 | description: 188 | name: test_api 189 | url: "https://pub.flutter-io.cn" 190 | source: hosted 191 | version: "0.2.4" 192 | transparent_image: 193 | dependency: "direct main" 194 | description: 195 | name: transparent_image 196 | url: "https://pub.flutter-io.cn" 197 | source: hosted 198 | version: "0.1.0" 199 | typed_data: 200 | dependency: transitive 201 | description: 202 | name: typed_data 203 | url: "https://pub.flutter-io.cn" 204 | source: hosted 205 | version: "1.1.6" 206 | vector_math: 207 | dependency: transitive 208 | description: 209 | name: vector_math 210 | url: "https://pub.flutter-io.cn" 211 | source: hosted 212 | version: "2.0.8" 213 | video_player: 214 | dependency: "direct main" 215 | description: 216 | name: video_player 217 | url: "https://pub.flutter-io.cn" 218 | source: hosted 219 | version: "0.5.4" 220 | sdks: 221 | dart: ">=2.2.0 <3.0.0" 222 | flutter: ">=1.2.0 <2.0.0" 223 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutterapp 2 | description: A new Flutter application. 3 | 4 | dependencies: 5 | flutter: 6 | sdk: flutter 7 | flutter_localizations: 8 | sdk: flutter 9 | 10 | # The following adds the Cupertino Icons font to your application. 11 | # Use with the CupertinoIcons class for iOS style icons. 12 | cupertino_icons: ^0.1.2 13 | english_words: ^3.1.0 14 | transparent_image: ^0.1.0 15 | pulltorefresh_flutter: ^0.1.7 16 | marquee_flutter: ^0.0.2 17 | dragablegridview_flutter: ^0.0.3 18 | camera: ^0.4.3+2 19 | path_provider: ^0.5.0 20 | video_player: ^0.5.2 21 | image_picker: ^0.5.3+1 22 | #cached_network_image: ^0.4.1 23 | 24 | dev_dependencies: 25 | flutter_test: 26 | sdk: flutter 27 | 28 | 29 | # For information on the generic Dart part of this file, see the 30 | # following page: https://www.dartlang.org/tools/pub/pubspec 31 | 32 | # The following section is specific to Flutter. 33 | flutter: 34 | 35 | # The following line ensures that the Material Icons font is 36 | # included with your application, so that you can use the icons in 37 | # the material Icons class. 38 | uses-material-design: true 39 | 40 | # To add assets to your application, add an assets section, like this: 41 | assets: 42 | - images/chaonan1.jpeg 43 | - images/chaonan2.jpg 44 | - images/chaonan3.jpeg 45 | - images/chaonan4.jpeg 46 | - images/refresh.png 47 | - images/icon_arrow.png 48 | - images/icon_cry.png 49 | - images/tianmao.jpg 50 | - images/close.png 51 | 52 | # An image asset can refer to one or more resolution-specific "variants", see 53 | # https://flutter.io/assets-and-images/#resolution-aware. 54 | 55 | # For details regarding adding assets from package dependencies, see 56 | # https://flutter.io/assets-and-images/#from-packages 57 | 58 | # To add custom fonts to your application, add a fonts section here, 59 | # in this "flutter" section. Each entry in this list should have a 60 | # "family" key with the font family name, and a "fonts" key with a 61 | # list giving the asset and other descriptors for the font. For 62 | # example: 63 | # fonts: 64 | # - family: Schyler 65 | # fonts: 66 | # - asset: fonts/Schyler-Regular.ttf 67 | # - asset: fonts/Schyler-Italic.ttf 68 | # style: italic 69 | # - family: Trajan Pro 70 | # fonts: 71 | # - asset: fonts/TrajanPro.ttf 72 | # - asset: fonts/TrajanPro_Bold.ttf 73 | # weight: 700 74 | # 75 | # For details regarding fonts from package dependencies, 76 | # see https://flutter.io/custom-fonts/#from-packages -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | double a=0.12; 4 | print(a.isNaN); 5 | } 6 | --------------------------------------------------------------------------------