├── example
├── android
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── app
│ │ ├── src
│ │ │ └── main
│ │ │ │ ├── res
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ └── ic_launcher.png
│ │ │ │ ├── values
│ │ │ │ │ └── styles.xml
│ │ │ │ └── drawable
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── example
│ │ │ │ │ └── MainActivity.java
│ │ │ │ └── AndroidManifest.xml
│ │ └── build.gradle
│ ├── .gitignore
│ ├── settings.gradle
│ ├── build.gradle
│ ├── gradlew.bat
│ └── gradlew
├── ios
│ ├── Flutter
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ └── AppFrameworkInfo.plist
│ ├── Runner
│ │ ├── AppDelegate.h
│ │ ├── Assets.xcassets
│ │ │ ├── LaunchImage.imageset
│ │ │ │ ├── LaunchImage.png
│ │ │ │ ├── LaunchImage@2x.png
│ │ │ │ ├── LaunchImage@3x.png
│ │ │ │ ├── README.md
│ │ │ │ └── Contents.json
│ │ │ └── AppIcon.appiconset
│ │ │ │ ├── Icon-App-20x20@1x.png
│ │ │ │ ├── Icon-App-20x20@2x.png
│ │ │ │ ├── Icon-App-20x20@3x.png
│ │ │ │ ├── Icon-App-29x29@1x.png
│ │ │ │ ├── Icon-App-29x29@2x.png
│ │ │ │ ├── Icon-App-29x29@3x.png
│ │ │ │ ├── Icon-App-40x40@1x.png
│ │ │ │ ├── Icon-App-40x40@2x.png
│ │ │ │ ├── Icon-App-40x40@3x.png
│ │ │ │ ├── Icon-App-60x60@2x.png
│ │ │ │ ├── Icon-App-60x60@3x.png
│ │ │ │ ├── Icon-App-76x76@1x.png
│ │ │ │ ├── Icon-App-76x76@2x.png
│ │ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ │ ├── Icon-App-83.5x83.5@2x.png
│ │ │ │ └── Contents.json
│ │ ├── main.m
│ │ ├── AppDelegate.m
│ │ ├── Info.plist
│ │ └── Base.lproj
│ │ │ ├── Main.storyboard
│ │ │ └── LaunchScreen.storyboard
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ ├── Runner.xcodeproj
│ │ ├── project.xcworkspace
│ │ │ └── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── xcschemes
│ │ │ │ └── Runner.xcscheme
│ │ └── project.pbxproj
│ └── .gitignore
├── .gitignore
├── README.md
├── .metadata
├── .idea
│ ├── runConfigurations
│ │ └── main_dart.xml
│ ├── libraries
│ │ ├── Flutter_for_Android.xml
│ │ └── Dart_SDK.xml
│ ├── modules.xml
│ └── workspace.xml
├── example.iml
├── test
│ └── widget_test.dart
├── lib
│ ├── MinList.dart
│ ├── main.dart
│ ├── SectionList.dart
│ └── FullList.dart
├── example_android.iml
└── pubspec.yaml
├── .gitattributes
├── screen_ios.png
├── screen_android.png
├── .idea
├── markdown-navigator
│ └── profiles_settings.xml
├── vcs.xml
├── libraries
│ ├── Flutter_Plugins.xml
│ ├── Dart_SDK.xml
│ └── Dart_Packages.xml
├── modules.xml
├── misc.xml
├── codeStyles
│ └── Project.xml
├── markdown-navigator.xml
└── workspace.xml
├── lib
├── pullrefresh
│ ├── pull_to_refresh.dart
│ └── src
│ │ ├── internals
│ │ ├── default_constants.dart
│ │ ├── indicator_config.dart
│ │ ├── refresh_physics.dart
│ │ └── indicator_wrap.dart
│ │ ├── indicator
│ │ └── classic_indicator.dart
│ │ └── smart_refresher.dart
└── flutter_section_table_view.dart
├── .gitignore
├── test
└── flutter_section_table_view_test.dart
├── CHANGELOG.md
├── LICENSE
├── flutter_section_table_view.iml
├── pubspec.yaml
└── README.md
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/screen_ios.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/screen_ios.png
--------------------------------------------------------------------------------
/screen_android.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/screen_android.png
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 |
7 | build/
8 |
9 | .flutter-plugins
10 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : FlutterAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # example
2 |
3 | A new Flutter project.
4 |
5 | ## Getting Started
6 |
7 | For help getting started with Flutter, view our online
8 | [documentation](https://flutter.io/).
9 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Realank/flutter_section_table_view/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/.idea/libraries/Flutter_Plugins.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
7 |
--------------------------------------------------------------------------------
/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: c7ea3ca377e909469c68f2ab878a5bc53d3cf66b
8 | channel: beta
9 |
--------------------------------------------------------------------------------
/lib/pullrefresh/pull_to_refresh.dart:
--------------------------------------------------------------------------------
1 | /*
2 | Author: Jpeng
3 | Email: peng8350@gmail.com
4 | createTime:2018-05-03 15:39
5 | */
6 |
7 | library pulltorefresh;
8 |
9 | export 'src/smart_refresher.dart';
10 | export 'src/indicator/classic_indicator.dart';
11 | export 'src/internals/indicator_config.dart';
12 |
--------------------------------------------------------------------------------
/example/.idea/runConfigurations/main_dart.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildSystemType
6 | Original
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/.idea/libraries/Flutter_for_Android.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/example/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.
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://www.dartlang.org/guides/libraries/private-files
2 |
3 | # Files and directories created by pub
4 | .dart_tool/
5 | .packages
6 | .pub/
7 | build/
8 | # If you're building an application, you may want to check-in your pubspec.lock
9 | pubspec.lock
10 |
11 | # Directory created by dartdoc
12 | # If you don't generate documentation locally you can remove this line.
13 | doc/api/
14 |
--------------------------------------------------------------------------------
/example/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/example/example/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.example;
2 |
3 | import android.os.Bundle;
4 | import io.flutter.app.FlutterActivity;
5 | import io.flutter.plugins.GeneratedPluginRegistrant;
6 |
7 | public class MainActivity extends FlutterActivity {
8 | @Override
9 | protected void onCreate(Bundle savedInstanceState) {
10 | super.onCreate(savedInstanceState);
11 | GeneratedPluginRegistrant.registerWith(this);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/test/flutter_section_table_view_test.dart:
--------------------------------------------------------------------------------
1 | //import 'package:test/test.dart';
2 | //
3 | ////import 'package:flutter_section_table_view/flutter_section_table_view.dart';
4 | //
5 | //void main() {
6 | // test('adds one to input values', () {
7 | //// final calculator = new Calculator();
8 | //// expect(calculator.addOne(2), 3);
9 | //// expect(calculator.addOne(-7), -6);
10 | //// expect(calculator.addOne(0), 1);
11 | //// expect(() => calculator.addOne(null), throwsNoSuchMethodError);
12 | // });
13 | //}
14 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | jcenter()
5 | }
6 |
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:3.0.1'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | google()
15 | jcenter()
16 | }
17 | }
18 |
19 | rootProject.buildDir = '../build'
20 | subprojects {
21 | project.buildDir = "${rootProject.buildDir}/${project.name}"
22 | }
23 | subprojects {
24 | project.evaluationDependsOn(':app')
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [0.0.1] - release first version
2 | ## [0.0.2] - change dart verstion
3 | ## [0.0.3] - add example
4 | ## [0.0.4] - add screen shot
5 | ## [0.0.5] - add animateTo/jumpTo feature
6 | ## [0.0.6] - add scroll detection
7 | ## [0.1.0] - fix bug and release 0.1.0
8 | ## [0.1.1] - change divider layout
9 | ## [0.2.0] - add pull up/down refresh
10 | ## [0.2.1] - fix readme
11 | ## [0.2.2] - fix readme
12 | ## [0.2.3] - format codes
13 | ## [0.2.4] - fix dependencies
14 | ## [0.2.5] - fix hint
15 | ## [1.0.0] - release stable version
16 | ## [1.0.1] - add always scroll physics
17 | ## [1.0.2] - re-format
18 | ## [1.0.3] - fix bug for empty list
--------------------------------------------------------------------------------
/lib/pullrefresh/src/internals/default_constants.dart:
--------------------------------------------------------------------------------
1 | /*
2 | Author: Jpeng
3 | Email: peng8350@gmail.com
4 | createTime:2018-05-17 10:39
5 | */
6 |
7 | const int default_completeDuration = 800;
8 |
9 | const double default_refresh_triggerDistance = 100.0;
10 |
11 | const double default_load_triggerDistance = 5.0;
12 |
13 | const double default_VisibleRange = 50.0;
14 |
15 | const bool default_AutoLoad = true;
16 |
17 | const bool default_enablePullDown = true;
18 |
19 | const bool default_enablePullUp = false;
20 |
21 | const bool default_BottomWhenBuild = true;
22 |
23 | const bool default_enableOverScroll = true;
24 |
25 | const int spaceAnimateMill = 300;
26 |
27 | const double minSpace = 0.000001;
28 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/example.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/libraries/Dart_SDK.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/example/.idea/libraries/Dart_SDK.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Liu Yanbo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter
3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to
4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties
5 | // are correct.
6 |
7 | import 'package:flutter/material.dart';
8 | import 'package:flutter_test/flutter_test.dart';
9 |
10 | import 'package:example/main.dart';
11 |
12 | void main() {
13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
14 | // Build our app and trigger a frame.
15 | await tester.pumpWidget(new MyApp());
16 |
17 | // Verify that our counter starts at 0.
18 | expect(find.text('0'), findsOneWidget);
19 | expect(find.text('1'), findsNothing);
20 |
21 | // Tap the '+' icon and trigger a frame.
22 | await tester.tap(find.byIcon(Icons.add));
23 | await tester.pump();
24 |
25 | // Verify that our counter has incremented.
26 | expect(find.text('0'), findsNothing);
27 | expect(find.text('1'), findsOneWidget);
28 | });
29 | }
30 |
--------------------------------------------------------------------------------
/example/lib/MinList.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_section_table_view/flutter_section_table_view.dart';
3 |
4 | class MinList extends StatelessWidget {
5 | @override
6 | Widget build(BuildContext context) {
7 | return Scaffold(
8 | appBar: AppBar(
9 | title: Text('Min List'),
10 | ),
11 | body: SafeArea(
12 | child: SectionTableView(
13 | sectionCount: 7,
14 | numOfRowInSection: (section) {
15 | return section == 0 ? 3 : 4;
16 | },
17 | cellAtIndexPath: (section, row) {
18 | return Container(
19 | height: 44.0,
20 | child: Center(
21 | child: Text('Cell $section $row'),
22 | ),
23 | );
24 | },
25 | headerInSection: (section) {
26 | return Container(
27 | height: 25.0,
28 | color: Colors.grey,
29 | child: Text('Header $section'),
30 | );
31 | },
32 | divider: Container(
33 | color: Colors.green,
34 | height: 1.0,
35 | ),
36 | ),
37 | ),
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/flutter_section_table_view.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
17 |
27 |
28 |
--------------------------------------------------------------------------------
/example/example_android.iml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/example/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 | example
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 |
--------------------------------------------------------------------------------
/example/.idea/workspace.xml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/lib/pullrefresh/src/internals/indicator_config.dart:
--------------------------------------------------------------------------------
1 | /*
2 | Author: Jpeng
3 | Email: peng8350@gmail.com
4 | createTime:2018-05-14 15:39
5 | */
6 |
7 | import 'default_constants.dart';
8 | import 'package:flutter/widgets.dart';
9 |
10 | typedef void OnRefresh(bool up);
11 | typedef void OnOffsetChange(bool up, double offset);
12 | typedef Widget IndicatorBuilder(BuildContext context, int mode);
13 |
14 | /*
15 | * This will use to configure the Wrapper action
16 | */
17 | abstract class Config {
18 | // How many distances should be dragged to trigger refresh
19 | final double triggerDistance;
20 |
21 | const Config({this.triggerDistance});
22 | }
23 |
24 | class RefreshConfig extends Config {
25 | // display time of success or failed
26 | final int completeDuration;
27 | // emptySpace height
28 | final double visibleRange;
29 | const RefreshConfig(
30 | {this.visibleRange: default_VisibleRange,
31 | double triggerDistance: default_refresh_triggerDistance,
32 | this.completeDuration: default_completeDuration})
33 | : super(triggerDistance: triggerDistance);
34 | }
35 |
36 | class LoadConfig extends Config {
37 | // if autoLoad when touch outside
38 | final bool autoLoad;
39 | // Whether the interface is at the bottom when the interface is loaded
40 | final bool bottomWhenBuild;
41 |
42 | final bool enableOverScroll;
43 | const LoadConfig({
44 | this.autoLoad: default_AutoLoad,
45 | this.bottomWhenBuild: default_BottomWhenBuild,
46 | this.enableOverScroll: default_enableOverScroll,
47 | double triggerDistance: default_load_triggerDistance,
48 | }) : super(triggerDistance: triggerDistance);
49 | }
50 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/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 27
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.example.example"
27 | minSdkVersion 16
28 | targetSdkVersion 27
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 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_section_table_view
2 | description: A iOS like table view (list view) including section, row, section header and divider, support pull refresh
3 | version: 1.0.3
4 | author: Realank
5 | homepage: https://github.com/Realank/flutter_section_table_view
6 | environment:
7 | sdk: ">=2.0.0-dev.28.0 <3.0.0"
8 |
9 | dependencies:
10 | flutter:
11 | sdk: flutter
12 |
13 | dev_dependencies:
14 | flutter_test:
15 | sdk: flutter
16 |
17 | # For information on the generic Dart part of this file, see the
18 | # following page: https://www.dartlang.org/tools/pub/pubspec
19 |
20 | # The following section is specific to Flutter.
21 | flutter:
22 |
23 | # To add assets to your package, add an assets section, like this:
24 | # assets:
25 | # - images/a_dot_burr.jpeg
26 | # - images/a_dot_ham.jpeg
27 | #
28 | # For details regarding assets in packages, see
29 | # https://flutter.io/assets-and-images/#from-packages
30 | #
31 | # An image asset can refer to one or more resolution-specific "variants", see
32 | # https://flutter.io/assets-and-images/#resolution-aware.
33 |
34 | # To add custom fonts to your package, add a fonts section here,
35 | # in this "flutter" section. Each entry in this list should have a
36 | # "family" key with the font family name, and a "fonts" key with a
37 | # list giving the asset and other descriptors for the font. For
38 | # example:
39 | # fonts:
40 | # - family: Schyler
41 | # fonts:
42 | # - asset: fonts/Schyler-Regular.ttf
43 | # - asset: fonts/Schyler-Italic.ttf
44 | # style: italic
45 | # - family: Trajan Pro
46 | # fonts:
47 | # - asset: fonts/TrajanPro.ttf
48 | # - asset: fonts/TrajanPro_Bold.ttf
49 | # weight: 700
50 | #
51 | # For details regarding fonts in packages, see
52 | # https://flutter.io/custom-fonts/#from-packages
53 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: example
2 | description: A new Flutter project.
3 |
4 | dependencies:
5 | flutter:
6 | sdk: flutter
7 |
8 | flutter_section_table_view:
9 | path: ../
10 |
11 |
12 | dev_dependencies:
13 | flutter_test:
14 | sdk: flutter
15 |
16 |
17 | # For information on the generic Dart part of this file, see the
18 | # following page: https://www.dartlang.org/tools/pub/pubspec
19 |
20 | # The following section is specific to Flutter.
21 | flutter:
22 |
23 | # The following line ensures that the Material Icons font is
24 | # included with your application, so that you can use the icons in
25 | # the material Icons class.
26 | uses-material-design: true
27 |
28 | # To add assets to your application, add an assets section, like this:
29 | # assets:
30 | # - images/a_dot_burr.jpeg
31 | # - images/a_dot_ham.jpeg
32 |
33 | # An image asset can refer to one or more resolution-specific "variants", see
34 | # https://flutter.io/assets-and-images/#resolution-aware.
35 |
36 | # For details regarding adding assets from package dependencies, see
37 | # https://flutter.io/assets-and-images/#from-packages
38 |
39 | # To add custom fonts to your application, add a fonts section here,
40 | # in this "flutter" section. Each entry in this list should have a
41 | # "family" key with the font family name, and a "fonts" key with a
42 | # list giving the asset and other descriptors for the font. For
43 | # example:
44 | # fonts:
45 | # - family: Schyler
46 | # fonts:
47 | # - asset: fonts/Schyler-Regular.ttf
48 | # - asset: fonts/Schyler-Italic.ttf
49 | # style: italic
50 | # - family: Trajan Pro
51 | # fonts:
52 | # - asset: fonts/TrajanPro.ttf
53 | # - asset: fonts/TrajanPro_Bold.ttf
54 | # weight: 700
55 | #
56 | # For details regarding fonts from package dependencies,
57 | # see https://flutter.io/custom-fonts/#from-packages
58 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'FullList.dart';
3 | import 'SectionList.dart';
4 | import 'MinList.dart';
5 |
6 | void main() => runApp(new MyApp());
7 |
8 | class MyApp extends StatelessWidget {
9 | // This widget is the root of your application.
10 | @override
11 | Widget build(BuildContext context) {
12 | return new MaterialApp(
13 | title: 'Flutter Demo',
14 | theme: new ThemeData(
15 | primarySwatch: Colors.blue,
16 | ),
17 | home: Scaffold(appBar: AppBar(title: Text('Example')), body: HomePage()),
18 | );
19 | }
20 | }
21 |
22 | class HomePage extends StatelessWidget {
23 | void goNext(context, page) {
24 | Navigator.of(context).push(MaterialPageRoute(builder: (context) => page));
25 | }
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | return ListView(
30 | children: [
31 | ListTile(
32 | contentPadding: EdgeInsets.all(10.0),
33 | leading: Text(
34 | 'Full List \n- Pull down/up refresh & scroll to indexPath',
35 | style: TextStyle(color: Colors.blue, fontSize: 17.0),
36 | ),
37 | onTap: () {
38 | goNext(context, FullList());
39 | },
40 | ),
41 | ListTile(
42 | contentPadding: EdgeInsets.all(10.0),
43 | leading: Text(
44 | 'Static List \n- Scroll to indexPath',
45 | style: TextStyle(color: Colors.blue, fontSize: 17.0),
46 | ),
47 | onTap: () {
48 | goNext(context, SectionList());
49 | },
50 | ),
51 | ListTile(
52 | contentPadding: EdgeInsets.all(10.0),
53 | leading: Text(
54 | 'Min List \n- Can\'t refresh and scroll to indexPath',
55 | style: TextStyle(color: Colors.blue, fontSize: 17.0),
56 | ),
57 | onTap: () {
58 | goNext(context, MinList());
59 | },
60 | ),
61 | ],
62 | );
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/example/lib/SectionList.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_section_table_view/flutter_section_table_view.dart';
3 |
4 | class SectionList extends StatelessWidget {
5 | final controller = SectionTableController(
6 | sectionTableViewScrollTo: (section, row, isScrollDown) {
7 | print('received scroll to $section $row scrollDown:$isScrollDown');
8 | });
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return Scaffold(
13 | appBar: AppBar(
14 | title: Text('Section List'),
15 | ),
16 | floatingActionButton: FloatingActionButton(
17 | child: Column(
18 | mainAxisSize: MainAxisSize.min,
19 | children: [Text('Scroll'), Icon(Icons.keyboard_arrow_down)],
20 | ),
21 | onPressed: () {
22 | controller.animateTo(2, -1).then((complete) {
23 | print('animated $complete');
24 | });
25 | }),
26 | body: SafeArea(
27 | child: SectionTableView(
28 | sectionCount: 5,
29 | numOfRowInSection: (section) {
30 | return section == 0 ? 3 : 4;
31 | },
32 | cellAtIndexPath: (section, row) {
33 | return Container(
34 | height: 44.0,
35 | child: Center(
36 | child: Text('Cell $section $row'),
37 | ),
38 | );
39 | },
40 | headerInSection: (section) {
41 | return Container(
42 | height: 25.0,
43 | color: Colors.grey,
44 | child: Text('Header $section'),
45 | );
46 | },
47 | divider: Container(
48 | color: Colors.green,
49 | height: 1.0,
50 | ),
51 | controller: controller, //SectionTableController
52 | sectionHeaderHeight: (section) => 25.0,
53 | dividerHeight: () => 1.0,
54 | cellHeightAtIndexPath: (section, row) => 44.0,
55 | ),
56 | ),
57 | );
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
15 |
19 |
26 |
30 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/example/lib/FullList.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'dart:async';
3 | import 'package:flutter_section_table_view/flutter_section_table_view.dart';
4 |
5 | class FullList extends StatefulWidget {
6 | @override
7 | _FullListState createState() => _FullListState();
8 | }
9 |
10 | class _FullListState extends State {
11 | @override
12 | void initState() {
13 | // TODO: implement initState
14 | super.initState();
15 | }
16 |
17 | @override
18 | void dispose() {
19 | // TODO: implement dispose
20 | super.dispose();
21 | }
22 |
23 | int sectionCount = 0;
24 | final controller = SectionTableController(sectionTableViewScrollTo: (section, row, isScrollDown) {
25 | print('received scroll to $section $row scrollDown:$isScrollDown');
26 | });
27 | final refreshController = RefreshController();
28 |
29 | Indicator refreshHeaderBuilder(BuildContext context, int mode) {
30 | return ClassicIndicator(
31 | mode: mode,
32 | releaseText: '释放以刷新',
33 | refreshingText: '刷新中...',
34 | completeText: '完成',
35 | failedText: '失败',
36 | idleText: '下拉以刷新',
37 | noDataText: '',
38 | );
39 | }
40 |
41 | Indicator refreshFooterBuilder(BuildContext context, int mode) {
42 | return ClassicIndicator(
43 | mode: mode,
44 | releaseText: '释放以加载',
45 | refreshingText: '加载中...',
46 | completeText: '加载完成',
47 | failedText: '加载失败',
48 | idleText: '上拉以加载',
49 | noDataText: '',
50 | idleIcon: const Icon(Icons.arrow_upward, color: Colors.grey),
51 | releaseIcon: const Icon(Icons.arrow_downward, color: Colors.grey),
52 | );
53 | }
54 |
55 | @override
56 | Widget build(BuildContext context) {
57 | return Scaffold(
58 | appBar: AppBar(
59 | title: Text('Full List'),
60 | ),
61 | floatingActionButton: FloatingActionButton(
62 | child: Column(
63 | mainAxisSize: MainAxisSize.min,
64 | children: [Text('Scroll'), Icon(Icons.keyboard_arrow_down)],
65 | ),
66 | onPressed: () {
67 | controller.animateTo(2, -1).then((complete) {
68 | print('animated $complete');
69 | });
70 | }),
71 | body: SafeArea(
72 | child: SectionTableView(
73 | refreshHeaderBuilder: refreshHeaderBuilder,
74 | refreshFooterBuilder: refreshFooterBuilder,
75 | enablePullDown: true,
76 | enablePullUp: true,
77 | onRefresh: (up) {
78 | print('on refresh $up');
79 |
80 | Future.delayed(const Duration(milliseconds: 2009)).then((val) {
81 | refreshController.sendBack(up, RefreshStatus.completed);
82 | setState(() {
83 | if (up) {
84 | sectionCount = 4;
85 | } else {
86 | sectionCount++;
87 | }
88 | });
89 | });
90 | },
91 | refreshController: refreshController,
92 | sectionCount: sectionCount,
93 | numOfRowInSection: (section) {
94 | return section == 0 ? 1 : 4;
95 | },
96 | cellAtIndexPath: (section, row) {
97 | return Container(
98 | height: 44.0,
99 | child: Center(
100 | child: Text('Cell $section $row'),
101 | ),
102 | );
103 | },
104 | headerInSection: (section) {
105 | return Container(
106 | height: 25.0,
107 | color: Colors.grey,
108 | child: Text('Header $section'),
109 | );
110 | },
111 | divider: Container(
112 | color: Colors.green,
113 | height: 1.0,
114 | ),
115 | controller: controller, //SectionTableController
116 | sectionHeaderHeight: (section) => 25.0,
117 | dividerHeight: () => 1.0,
118 | cellHeightAtIndexPath: (section, row) => 44.0,
119 | ),
120 | ),
121 | );
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/lib/pullrefresh/src/indicator/classic_indicator.dart:
--------------------------------------------------------------------------------
1 | /**
2 | Author: Jpeng
3 | Email: peng8350@gmail.com
4 | createTime:2018-05-14 17:39
5 | */
6 |
7 | import 'package:flutter/material.dart' hide RefreshIndicator;
8 | import 'package:flutter/widgets.dart';
9 | import '../../pull_to_refresh.dart';
10 |
11 | enum IconPosition { left, right, top, bottom }
12 |
13 | class ClassicIndicator extends Indicator {
14 | final String releaseText,
15 | idleText,
16 | refreshingText,
17 | completeText,
18 | failedText,
19 | noDataText;
20 |
21 | final Widget releaseIcon,
22 | idleIcon,
23 | refreshingIcon,
24 | completeIcon,
25 | failedIcon,
26 | noMoreIcon;
27 |
28 | final double height;
29 |
30 | final double spacing;
31 |
32 | final IconPosition iconPos;
33 |
34 | final TextStyle textStyle;
35 |
36 | const ClassicIndicator({
37 | @required int mode,
38 | Key key,
39 | this.textStyle: const TextStyle(color: const Color(0xff555555)),
40 | this.releaseText: 'Refresh when release',
41 | this.refreshingText: 'Refreshing...',
42 | this.completeText: 'Refresh complete',
43 | this.noDataText: 'No more data',
44 | this.height: 60.0,
45 | this.noMoreIcon: const Icon(Icons.clear, color: Colors.grey),
46 | this.failedText: 'Refresh failed',
47 | this.idleText: 'Pull down to refresh',
48 | this.iconPos: IconPosition.left,
49 | this.spacing: 15.0,
50 | this.refreshingIcon: const CircularProgressIndicator(strokeWidth: 2.0),
51 | this.failedIcon: const Icon(Icons.clear, color: Colors.grey),
52 | this.completeIcon: const Icon(Icons.done, color: Colors.grey),
53 | this.idleIcon = const Icon(Icons.arrow_downward, color: Colors.grey),
54 | this.releaseIcon = const Icon(Icons.arrow_upward, color: Colors.grey),
55 | }) : super(key: key, mode: mode);
56 |
57 | @override
58 | State createState() {
59 | return new _ClassicIndicatorState();
60 | }
61 | }
62 |
63 | class _ClassicIndicatorState extends State {
64 | Widget _buildText() {
65 | return new Text(
66 | widget.mode == RefreshStatus.canRefresh
67 | ? widget.releaseText
68 | : widget.mode == RefreshStatus.completed
69 | ? widget.completeText
70 | : widget.mode == RefreshStatus.failed
71 | ? widget.failedText
72 | : widget.mode == RefreshStatus.refreshing
73 | ? widget.refreshingText
74 | : widget.mode == RefreshStatus.noMore
75 | ? widget.noDataText
76 | : widget.idleText,
77 | style: widget.textStyle);
78 | }
79 |
80 | Widget _buildIcon() {
81 | Widget icon = widget.mode == RefreshStatus.canRefresh
82 | ? widget.releaseIcon
83 | : widget.mode == RefreshStatus.noMore
84 | ? widget.noMoreIcon
85 | : widget.mode == RefreshStatus.idle
86 | ? widget.idleIcon
87 | : widget.mode == RefreshStatus.completed
88 | ? widget.completeIcon
89 | : widget.mode == RefreshStatus.failed
90 | ? widget.failedIcon
91 | : new SizedBox(
92 | width: 25.0,
93 | height: 25.0,
94 | child: widget.refreshingIcon,
95 | );
96 | return icon;
97 | }
98 |
99 | @override
100 | Widget build(BuildContext context) {
101 | // TODO: implement buildContent
102 | Widget textWidget = _buildText();
103 | Widget iconWidget = _buildIcon();
104 | List children = [
105 | iconWidget,
106 | new Container(
107 | width: widget.spacing,
108 | height: widget.spacing,
109 | ),
110 | textWidget
111 | ];
112 | Widget container = (widget.iconPos == IconPosition.top ||
113 | widget.iconPos == IconPosition.bottom)
114 | ? new Column(
115 | mainAxisAlignment: MainAxisAlignment.center,
116 | verticalDirection: widget.iconPos == IconPosition.top
117 | ? VerticalDirection.down
118 | : VerticalDirection.up,
119 | children: children,
120 | )
121 | : new Row(
122 | textDirection: widget.iconPos == IconPosition.right
123 | ? TextDirection.rtl
124 | : TextDirection.ltr,
125 | mainAxisAlignment: MainAxisAlignment.center,
126 | children: children,
127 | );
128 | return new Container(
129 | alignment: Alignment.center,
130 | height: widget.height,
131 | child: new Center(
132 | child: container,
133 | ),
134 | );
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/.idea/markdown-navigator.xml:
--------------------------------------------------------------------------------
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 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/example/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 |
--------------------------------------------------------------------------------
/lib/pullrefresh/src/internals/refresh_physics.dart:
--------------------------------------------------------------------------------
1 | /*
2 | Author: Jpeng
3 | Email: peng8350@gmail.com
4 | createTime:2018-05-02 14:39
5 | */
6 |
7 | import 'package:flutter/widgets.dart';
8 | import 'dart:math' as math;
9 | import 'package:flutter/material.dart';
10 |
11 | /*
12 | this class is copy from BouncingScrollPhysics,
13 | because it doesn't fit my idea,
14 | Fixed the problem that child parts could not be dragged without data.
15 | */
16 | class RefreshScrollPhysics extends ScrollPhysics {
17 | final bool enableOverScroll;
18 |
19 | /// Creates scroll physics that bounce back from the edge.
20 | const RefreshScrollPhysics(
21 | {ScrollPhysics parent, this.enableOverScroll: true})
22 | : super(parent: parent);
23 |
24 | @override
25 | RefreshScrollPhysics applyTo(ScrollPhysics ancestor) {
26 | return new RefreshScrollPhysics(
27 | parent: buildParent(ancestor), enableOverScroll: enableOverScroll);
28 | }
29 |
30 | /// The multiple applied to overscroll to make it appear that scrolling past
31 | /// the edge of the scrollable contents is harder than scrolling the list.
32 | /// This is done by reducing the ratio of the scroll effect output vs the
33 | /// scroll gesture input.
34 | ///
35 | /// This factor starts at 0.52 and progressively becomes harder to overscroll
36 | /// as more of the area past the edge is dragged in (represented by an increasing
37 | /// `overscrollFraction` which starts at 0 when there is no overscroll).
38 | double frictionFactor(double overscrollFraction) =>
39 | 0.52 * math.pow(1 - overscrollFraction, 2);
40 |
41 | @override
42 | bool shouldAcceptUserOffset(ScrollMetrics position) {
43 | // TODO: implement shouldAcceptUserOffset
44 | return true;
45 | }
46 |
47 | @override
48 | double applyPhysicsToUserOffset(ScrollMetrics position, double offset) {
49 | assert(offset != 0.0);
50 | assert(position.minScrollExtent <= position.maxScrollExtent);
51 |
52 | if (!position.outOfRange) return offset;
53 |
54 | final double overscrollPastStart =
55 | math.max(position.minScrollExtent - position.pixels, 0.0);
56 | final double overscrollPastEnd =
57 | math.max(position.pixels - position.maxScrollExtent, 0.0);
58 | final double overscrollPast =
59 | math.max(overscrollPastStart, overscrollPastEnd);
60 | final bool easing = (overscrollPastStart > 0.0 && offset < 0.0) ||
61 | (overscrollPastEnd > 0.0 && offset > 0.0);
62 |
63 | final double friction = easing
64 | // Apply less resistance when easing the overscroll vs tensioning.
65 | ? frictionFactor(
66 | (overscrollPast - offset.abs()) / position.viewportDimension)
67 | : frictionFactor(overscrollPast / position.viewportDimension);
68 | final double direction = offset.sign;
69 | return direction * _applyFriction(overscrollPast, offset.abs(), friction);
70 | }
71 |
72 | static double _applyFriction(
73 | double extentOutside, double absDelta, double gamma) {
74 | assert(absDelta > 0);
75 | double total = 0.0;
76 | if (extentOutside > 0) {
77 | final double deltaToLimit = extentOutside / gamma;
78 | if (absDelta < deltaToLimit) return absDelta * gamma;
79 | total += extentOutside;
80 | absDelta -= deltaToLimit;
81 | }
82 | return total + absDelta;
83 | }
84 |
85 | @override
86 | double applyBoundaryConditions(ScrollMetrics position, double value) {
87 | if (!enableOverScroll) {
88 | if (value < position.pixels &&
89 | position.pixels <= position.minScrollExtent) // underscroll
90 | return value - position.pixels;
91 | if (value < position.minScrollExtent &&
92 | position.minScrollExtent < position.pixels) // hit top edge
93 | return value - position.minScrollExtent;
94 | if (position.maxScrollExtent <= position.pixels &&
95 | position.pixels < value) // overscroll
96 | return value - position.pixels;
97 |
98 | if (position.pixels < position.maxScrollExtent &&
99 | position.maxScrollExtent < value) // hit bottom edge
100 | return value - position.maxScrollExtent;
101 | }
102 | return 0.0;
103 | }
104 |
105 | @override
106 | Simulation createBallisticSimulation(
107 | ScrollMetrics position, double velocity) {
108 | final Tolerance tolerance = this.tolerance;
109 | if (velocity.abs() >= tolerance.velocity || position.outOfRange) {
110 | return new BouncingScrollSimulation(
111 | spring: spring,
112 | position: position.pixels,
113 | velocity: velocity *
114 | 0.91, // TODO(abarth): We should move this constant closer to the drag end.
115 | leadingExtent: position.minScrollExtent,
116 | trailingExtent: position.maxScrollExtent,
117 | tolerance: tolerance,
118 | );
119 | }
120 | return null;
121 | }
122 |
123 | // The ballistic simulation here decelerates more slowly than the one for
124 | // ClampingScrollPhysics so we require a more deliberate input gesture
125 | // to trigger a fling.
126 | @override
127 | double get minFlingVelocity => 2.5 * 2.0;
128 |
129 | // Methodology:
130 | // 1- Use https://github.com/flutter/scroll_overlay to test with Flutter and
131 | // platform scroll views superimposed.
132 | // 2- Record incoming speed and make rapid flings in the test app.
133 | // 3- If the scrollables stopped overlapping at any moment, adjust the desired
134 | // output value of this function at that input speed.
135 | // 4- Feed new input/output set into a power curve fitter. Change function
136 | // and repeat from 2.
137 | // 5- Repeat from 2 with medium and slow flings.
138 | /// Momentum build-up function that mimics iOS's scroll speed increase with repeated flings.
139 | ///
140 | /// The velocity of the last fling is not an important factor. Existing speed
141 | /// and (related) time since last fling are factors for the velocity transfer
142 | /// calculations.
143 | @override
144 | double carriedMomentum(double existingVelocity) {
145 | return existingVelocity.sign *
146 | math.min(0.000816 * math.pow(existingVelocity.abs(), 1.967).toDouble(),
147 | 40000.0);
148 | }
149 |
150 | // Eyeballed from observation to counter the effect of an unintended scroll
151 | // from the natural motion of lifting the finger after a scroll.
152 | @override
153 | double get dragStartDistanceMotionThreshold => 3.5;
154 | }
155 |
--------------------------------------------------------------------------------
/.idea/libraries/Dart_Packages.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # flutter_section_table_view
2 |
3 | - A iOS like table view including section, row, section header and divider
4 | - Support both Android and iOS
5 | - Support drop-down refresh and pull-up load (based on [pull_to_refresh](https://pub.dartlang.org/packages/pull_to_refresh))
6 | - you can animate/jump to specific index path
7 | - you can know which index path it scrolled to, when scrolling
8 |
9 |
10 | ## Usage
11 |
12 | #### minimal
13 | ```dart
14 | class MinList extends StatelessWidget {
15 | @override
16 | Widget build(BuildContext context) {
17 | return Scaffold(
18 | appBar: AppBar(
19 | title: Text('Min List'),
20 | ),
21 | body: SafeArea(
22 | child: SectionTableView(
23 | sectionCount: 7,
24 | numOfRowInSection: (section) {
25 | return section == 0 ? 3 : 4;
26 | },
27 | cellAtIndexPath: (section, row) {
28 | return Container(
29 | height: 44.0,
30 | child: Center(
31 | child: Text('Cell $section $row'),
32 | ),
33 | );
34 | },
35 | headerInSection: (section) {
36 | return Container(
37 | height: 25.0,
38 | color: Colors.grey,
39 | child: Text('Header $section'),
40 | );
41 | },
42 | divider: Container(
43 | color: Colors.green,
44 | height: 1.0,
45 | ),
46 | ),
47 | ),
48 | );
49 | }
50 | }
51 |
52 | ```
53 |
54 | #### Section List which can scroll to specific index path
55 |
56 | ```dart
57 |
58 | class SectionList extends StatelessWidget {
59 | final controller = SectionTableController(sectionTableViewScrollTo: (section, row, isScrollDown) {
60 | print('received scroll to $section $row scrollDown:$isScrollDown');
61 | });
62 |
63 | @override
64 | Widget build(BuildContext context) {
65 | return Scaffold(
66 | appBar: AppBar(
67 | title: Text('Section List'),
68 | ),
69 | floatingActionButton: FloatingActionButton(
70 | child: Column(
71 | mainAxisSize: MainAxisSize.min,
72 | children: [Text('Scroll'), Icon(Icons.keyboard_arrow_down)],
73 | ),
74 | onPressed: () {
75 | controller.animateTo(2, -1).then((complete) {
76 | print('animated $complete');
77 | });
78 | }),
79 | body: SafeArea(
80 | child: SectionTableView(
81 | sectionCount: 7,
82 | numOfRowInSection: (section) {
83 | return section == 0 ? 3 : 4;
84 | },
85 | cellAtIndexPath: (section, row) {
86 | return Container(
87 | height: 44.0,
88 | child: Center(
89 | child: Text('Cell $section $row'),
90 | ),
91 | );
92 | },
93 | headerInSection: (section) {
94 | return Container(
95 | height: 25.0,
96 | color: Colors.grey,
97 | child: Text('Header $section'),
98 | );
99 | },
100 | divider: Container(
101 | color: Colors.green,
102 | height: 1.0,
103 | ),
104 | controller: controller, //SectionTableController
105 | sectionHeaderHeight: (section) => 25.0,
106 | dividerHeight: () => 1.0,
107 | cellHeightAtIndexPath: (section, row) => 44.0,
108 | ),
109 | ),
110 | );
111 | }
112 | }
113 |
114 | ```
115 |
116 | #### Full function list which can pull up/down refresh
117 | ```dart
118 | class FullList extends StatefulWidget {
119 | @override
120 | _FullListState createState() => _FullListState();
121 | }
122 |
123 | class _FullListState extends State {
124 | @override
125 | void initState() {
126 | // TODO: implement initState
127 | super.initState();
128 | }
129 |
130 | @override
131 | void dispose() {
132 | // TODO: implement dispose
133 | super.dispose();
134 | }
135 |
136 | int sectionCount = 9;
137 | final controller = SectionTableController(sectionTableViewScrollTo: (section, row, isScrollDown) {
138 | print('received scroll to $section $row scrollDown:$isScrollDown');
139 | });
140 | final refreshController = RefreshController();
141 |
142 | Indicator refreshHeaderBuilder(BuildContext context, int mode) {
143 | return ClassicIndicator(
144 | mode: mode,
145 | releaseText: '释放以刷新',
146 | refreshingText: '刷新中...',
147 | completeText: '完成',
148 | failedText: '失败',
149 | idleText: '下拉以刷新',
150 | noDataText: '',
151 | );
152 | }
153 |
154 | Indicator refreshFooterBuilder(BuildContext context, int mode) {
155 | return ClassicIndicator(
156 | mode: mode,
157 | releaseText: '释放以加载',
158 | refreshingText: '加载中...',
159 | completeText: '加载完成',
160 | failedText: '加载失败',
161 | idleText: '上拉以加载',
162 | noDataText: '',
163 | idleIcon: const Icon(Icons.arrow_upward, color: Colors.grey),
164 | releaseIcon: const Icon(Icons.arrow_downward, color: Colors.grey),
165 | );
166 | }
167 |
168 | @override
169 | Widget build(BuildContext context) {
170 | return Scaffold(
171 | appBar: AppBar(
172 | title: Text('Full List'),
173 | ),
174 | floatingActionButton: FloatingActionButton(
175 | child: Column(
176 | mainAxisSize: MainAxisSize.min,
177 | children: [Text('Scroll'), Icon(Icons.keyboard_arrow_down)],
178 | ),
179 | onPressed: () {
180 | controller.animateTo(2, -1).then((complete) {
181 | print('animated $complete');
182 | });
183 | }),
184 | body: SafeArea(
185 | child: SectionTableView(
186 | refreshHeaderBuilder: refreshHeaderBuilder,
187 | refreshFooterBuilder: refreshFooterBuilder,
188 | enablePullDown: true,
189 | enablePullUp: true,
190 | onRefresh: (up) {
191 | print('on refresh $up');
192 |
193 | Future.delayed(const Duration(milliseconds: 2009)).then((val) {
194 | refreshController.sendBack(up, RefreshStatus.completed);
195 | setState(() {
196 | if (up) {
197 | sectionCount = 5;
198 | } else {
199 | sectionCount++;
200 | }
201 | });
202 | });
203 | },
204 | refreshController: refreshController,
205 | sectionCount: sectionCount,
206 | numOfRowInSection: (section) {
207 | return section == 0 ? 3 : 4;
208 | },
209 | cellAtIndexPath: (section, row) {
210 | return Container(
211 | height: 44.0,
212 | child: Center(
213 | child: Text('Cell $section $row'),
214 | ),
215 | );
216 | },
217 | headerInSection: (section) {
218 | return Container(
219 | height: 25.0,
220 | color: Colors.grey,
221 | child: Text('Header $section'),
222 | );
223 | },
224 | divider: Container(
225 | color: Colors.green,
226 | height: 1.0,
227 | ),
228 | controller: controller, //SectionTableController
229 | sectionHeaderHeight: (section) => 25.0,
230 | dividerHeight: () => 1.0,
231 | cellHeightAtIndexPath: (section, row) => 44.0,
232 | ),
233 | ),
234 | );
235 | }
236 | }
237 |
238 |
239 | ```
240 |
241 |
242 | | iOS | android |
243 | | --- | ------- |
244 | |  | |
245 |
246 |
247 |
248 | ## Getting Started
249 |
250 | For help getting started with Flutter, view our online [documentation](https://flutter.io/).
251 |
252 | For help on editing package code, view the [documentation](https://flutter.io/developing-packages/).
253 |
--------------------------------------------------------------------------------
/lib/pullrefresh/src/internals/indicator_wrap.dart:
--------------------------------------------------------------------------------
1 | /*
2 | Author: Jpeng
3 | Email: peng8350@gmail.com
4 | createTime:2018-05-14 15:39
5 | */
6 |
7 | import 'dart:async';
8 | import 'package:flutter/widgets.dart';
9 | import 'package:flutter/material.dart';
10 | import 'default_constants.dart';
11 | import '../../pull_to_refresh.dart';
12 |
13 | abstract class Wrapper extends StatefulWidget {
14 | final ValueNotifier modeListener;
15 |
16 | final IndicatorBuilder builder;
17 |
18 | final bool up;
19 |
20 | final double triggerDistance;
21 |
22 | bool get _isRefreshing => this.mode == RefreshStatus.refreshing;
23 |
24 | bool get _isComplete =>
25 | this.mode != RefreshStatus.idle &&
26 | this.mode != RefreshStatus.refreshing &&
27 | this.mode != RefreshStatus.canRefresh;
28 |
29 | int get mode => this.modeListener.value;
30 |
31 | set mode(int mode) => this.modeListener.value = mode;
32 |
33 | Wrapper(
34 | {Key key,
35 | @required this.up,
36 | @required this.modeListener,
37 | this.builder,
38 | this.triggerDistance})
39 | : assert(up != null, modeListener != null),
40 | super(key: key);
41 |
42 | bool _isScrollToOutSide(ScrollNotification notification) {
43 | if (up) {
44 | if (notification.metrics.minScrollExtent - notification.metrics.pixels >
45 | 0) {
46 | return true;
47 | }
48 | } else {
49 | if (notification.metrics.pixels - notification.metrics.maxScrollExtent >
50 | 0) {
51 | return true;
52 | }
53 | }
54 | return false;
55 | }
56 | }
57 |
58 | //idle,refreshing,completed,failed,canRefresh
59 | class RefreshWrapper extends Wrapper {
60 | final int completeDuration;
61 |
62 | final Function onOffsetChange;
63 |
64 | final double visibleRange;
65 |
66 | RefreshWrapper({
67 | Key key,
68 | IndicatorBuilder builder,
69 | ValueNotifier modeLis,
70 | this.onOffsetChange,
71 | this.completeDuration: default_completeDuration,
72 | double triggerDistance,
73 | this.visibleRange: default_VisibleRange,
74 | bool up: true,
75 | }) : assert(up != null),
76 | super(
77 | up: up,
78 | key: key,
79 | modeListener: modeLis,
80 | builder: builder,
81 | triggerDistance: triggerDistance,
82 | );
83 |
84 | @override
85 | State createState() {
86 | // TODO: implement createState
87 | return new RefreshWrapperState();
88 | }
89 | }
90 |
91 | class RefreshWrapperState extends State
92 | with TickerProviderStateMixin
93 | implements GestureProcessor {
94 | AnimationController _sizeController;
95 |
96 | /*
97 | up indicate drag from top (pull down)
98 | */
99 | void _dismiss() {
100 | /*
101 | why the value is 0.00001?
102 | If this value is 0, no controls will
103 | cause Flutter to automatically retrieve widget.
104 | */
105 | _sizeController.animateTo(minSpace).then((dynamic val) {
106 | widget.mode = RefreshStatus.idle;
107 | });
108 | }
109 |
110 | int get mode => widget.modeListener.value;
111 |
112 | double _measure(ScrollNotification notification) {
113 | if (widget.up) {
114 | return (notification.metrics.minScrollExtent -
115 | notification.metrics.pixels) /
116 | widget.triggerDistance;
117 | } else {
118 | return (notification.metrics.pixels -
119 | notification.metrics.maxScrollExtent) /
120 | widget.triggerDistance;
121 | }
122 | }
123 |
124 | @override
125 | void onDragStart(ScrollStartNotification notification) {
126 | // TODO: implement onDragStart
127 | }
128 |
129 | @override
130 | void onDragMove(ScrollUpdateNotification notification) {
131 | // TODO: implement onDragMove
132 | if (!widget._isScrollToOutSide(notification)) {
133 | return;
134 | }
135 | if (widget._isComplete || widget._isRefreshing) return;
136 |
137 | double offset = _measure(notification);
138 | if (offset >= 1.0) {
139 | widget.mode = RefreshStatus.canRefresh;
140 | } else {
141 | widget.mode = RefreshStatus.idle;
142 | }
143 | }
144 |
145 | @override
146 | void onDragEnd(ScrollNotification notification) {
147 | // TODO: implement onDragEnd
148 | if (!widget._isScrollToOutSide(notification)) {
149 | return;
150 | }
151 | if (widget._isComplete || widget._isRefreshing) return;
152 | bool reachMax = _measure(notification) >= 1.0;
153 | if (!reachMax) {
154 | _sizeController.animateTo(0.0);
155 | return;
156 | } else {
157 | widget.mode = RefreshStatus.refreshing;
158 | }
159 | }
160 |
161 | void _handleOffsetCallBack() {
162 | if (widget.onOffsetChange != null) {
163 | widget.onOffsetChange(
164 | widget.up, _sizeController.value * widget.visibleRange);
165 | }
166 | }
167 |
168 | void _handleModeChange() {
169 | switch (mode) {
170 | case RefreshStatus.refreshing:
171 | _sizeController.value = 1.0;
172 | break;
173 | case RefreshStatus.completed:
174 | new Future.delayed(new Duration(milliseconds: widget.completeDuration),
175 | () {
176 | _dismiss();
177 | });
178 | break;
179 | case RefreshStatus.failed:
180 | new Future.delayed(new Duration(milliseconds: widget.completeDuration),
181 | () {
182 | _dismiss();
183 | }).then((val) {
184 | widget.mode = RefreshStatus.idle;
185 | });
186 | break;
187 | }
188 | setState(() {});
189 | }
190 |
191 | @override
192 | void dispose() {
193 | // TODO: implement dispose
194 | widget.modeListener.removeListener(_handleModeChange);
195 | _sizeController.removeListener(_handleOffsetCallBack);
196 | super.dispose();
197 | }
198 |
199 | @override
200 | void initState() {
201 | // TODO: implement initState
202 | super.initState();
203 | this._sizeController = new AnimationController(
204 | vsync: this,
205 | lowerBound: minSpace,
206 | duration: const Duration(milliseconds: spaceAnimateMill))
207 | ..addListener(_handleOffsetCallBack);
208 | widget.modeListener.addListener(_handleModeChange);
209 | }
210 |
211 | @override
212 | Widget build(BuildContext context) {
213 | // TODO: implement build
214 | if (widget.up) {
215 | return new Column(
216 | children: [
217 | new SizeTransition(
218 | sizeFactor: _sizeController,
219 | child: new Container(height: widget.visibleRange),
220 | ),
221 | widget.builder(context, widget.mode)
222 | ],
223 | );
224 | }
225 | return new Column(
226 | children: [
227 | widget.builder(context, widget.mode),
228 | new SizeTransition(
229 | sizeFactor: _sizeController,
230 | child: new Container(height: widget.visibleRange),
231 | )
232 | ],
233 | );
234 | }
235 | }
236 |
237 | //status: failed,nomore,idle,refreshing
238 | class LoadWrapper extends Wrapper {
239 | final bool autoLoad;
240 |
241 | LoadWrapper(
242 | {Key key,
243 | @required bool up,
244 | @required ValueNotifier modeListener,
245 | double triggerDistance,
246 | this.autoLoad,
247 | IndicatorBuilder builder})
248 | : assert(up != null, modeListener != null),
249 | super(
250 | key: key,
251 | up: up,
252 | builder: builder,
253 | modeListener: modeListener,
254 | triggerDistance: triggerDistance,
255 | );
256 |
257 | @override
258 | State createState() {
259 | // TODO: implement createState
260 | return new LoadWrapperState();
261 | }
262 | }
263 |
264 | class LoadWrapperState extends State implements GestureProcessor {
265 | Function _updateListener;
266 |
267 | @override
268 | Widget build(BuildContext context) {
269 | // TODO: implement build
270 | return widget.builder(context, widget.mode);
271 | }
272 |
273 | @override
274 | void initState() {
275 | // TODO: implement initState
276 | super.initState();
277 | _updateListener = () {
278 | setState(() {});
279 | };
280 | widget.modeListener.addListener(_updateListener);
281 | }
282 |
283 | @override
284 | void dispose() {
285 | // TODO: implement dispose
286 | widget.modeListener.removeListener(_updateListener);
287 | super.dispose();
288 | }
289 |
290 | @override
291 | void onDragStart(ScrollStartNotification notification) {
292 | // TODO: implement onDragStart
293 | }
294 |
295 | @override
296 | void onDragMove(ScrollUpdateNotification notification) {
297 | // TODO: implement onDragMove
298 | // if (!widget._isScrollToOutSide(notification)) {
299 | // return;
300 | // }
301 | if (widget._isRefreshing || widget._isComplete) return;
302 | if (widget.autoLoad) {
303 | if (widget.up &&
304 | notification.metrics.extentBefore <= widget.triggerDistance)
305 | widget.mode = RefreshStatus.refreshing;
306 | if (!widget.up &&
307 | notification.metrics.extentAfter <= widget.triggerDistance)
308 | widget.mode = RefreshStatus.refreshing;
309 | }
310 | }
311 |
312 | @override
313 | void onDragEnd(ScrollNotification notification) {
314 | // TODO: implement onDragEnd
315 | if (widget._isRefreshing || widget._isComplete) return;
316 | if (widget.autoLoad) {
317 | if (widget.up &&
318 | notification.metrics.extentBefore <= widget.triggerDistance)
319 | widget.mode = RefreshStatus.refreshing;
320 | if (!widget.up &&
321 | notification.metrics.extentAfter <= widget.triggerDistance)
322 | widget.mode = RefreshStatus.refreshing;
323 | }
324 | }
325 | }
326 |
327 | abstract class GestureProcessor {
328 | void onDragStart(ScrollStartNotification notification);
329 |
330 | void onDragMove(ScrollUpdateNotification notification);
331 |
332 | void onDragEnd(ScrollNotification notification);
333 | }
334 |
--------------------------------------------------------------------------------
/lib/pullrefresh/src/smart_refresher.dart:
--------------------------------------------------------------------------------
1 | /*
2 | Author: Jpeng
3 | Email: peng8350@gmail.com
4 | createTime:2018-05-01 11:39
5 | */
6 |
7 | import 'package:flutter/material.dart';
8 | import 'package:flutter/scheduler.dart';
9 | import 'internals/default_constants.dart';
10 | import 'package:flutter/widgets.dart';
11 | import 'package:flutter/foundation.dart';
12 | import 'internals/indicator_config.dart';
13 | import 'internals/indicator_wrap.dart';
14 | import 'internals/refresh_physics.dart';
15 | import 'indicator/classic_indicator.dart';
16 | import 'dart:math' as math;
17 | import 'dart:async';
18 |
19 | enum WrapperType { Refresh, Loading }
20 |
21 | class RefreshStatus {
22 | static const int idle = 0;
23 | static const int canRefresh = 1;
24 | static const int refreshing = 2;
25 | static const int completed = 3;
26 | static const int failed = 4;
27 | static const int noMore = 5;
28 | }
29 |
30 | /*
31 | This is the most important component that provides drop-down refresh and up loading.
32 | */
33 | class SmartRefresher extends StatefulWidget {
34 | //indicate your listView
35 | final ScrollView child;
36 |
37 | final IndicatorBuilder headerBuilder;
38 | final IndicatorBuilder footerBuilder;
39 | // configure your header and footer
40 | final Config headerConfig, footerConfig;
41 | // This bool will affect whether or not to have the function of drop-up load.
42 | final bool enablePullUp;
43 | //This bool will affect whether or not to have the function of drop-down refresh.
44 | final bool enablePullDown;
45 | // if open OverScroll if you use RefreshIndicator and LoadFooter
46 | final bool enableOverScroll;
47 | // upper and downer callback when you drag out of the distance
48 | final OnRefresh onRefresh;
49 | // This method will callback when the indicator changes from edge to edge.
50 | final OnOffsetChange onOffsetChange;
51 | //controll inner state
52 | final RefreshController controller;
53 |
54 | SmartRefresher({
55 | Key key,
56 | @required this.child,
57 | IndicatorBuilder headerBuilder,
58 | IndicatorBuilder footerBuilder,
59 | RefreshController controller,
60 | this.headerConfig: const RefreshConfig(),
61 | this.footerConfig: const LoadConfig(),
62 | this.enableOverScroll: default_enableOverScroll,
63 | this.enablePullDown: default_enablePullDown,
64 | this.enablePullUp: default_enablePullUp,
65 | this.onRefresh,
66 | this.onOffsetChange,
67 | }) : assert(child != null),
68 | controller = controller ?? new RefreshController(),
69 | this.headerBuilder = headerBuilder ??
70 | ((BuildContext context, int mode) {
71 | return new ClassicIndicator(mode: mode);
72 | }),
73 | this.footerBuilder = footerBuilder ??
74 | ((BuildContext context, int mode) {
75 | return new ClassicIndicator(mode: mode);
76 | }),
77 | super(key: key);
78 |
79 | @override
80 | _SmartRefresherState createState() => new _SmartRefresherState();
81 | }
82 |
83 | class _SmartRefresherState extends State {
84 | // listen the listen offset or on...
85 | ScrollController get _scrollController => widget.controller.scrollController;
86 | // the bool will check the user if dragging on the screen.
87 | // bool _isDragging = false;
88 | // key to get height header of footer
89 | final GlobalKey _headerKey = new GlobalKey(), _footerKey = new GlobalKey();
90 | // the height must be equals your headerBuilder
91 | double _headerHeight = 0.0, _footerHeight = 0.0;
92 |
93 | ValueNotifier offsetLis = new ValueNotifier(0.0);
94 |
95 | ValueNotifier topModeLis = new ValueNotifier(0);
96 |
97 | ValueNotifier bottomModeLis = new ValueNotifier(0);
98 |
99 | //handle the scrollStartEvent
100 | bool _handleScrollStart(ScrollStartNotification notification) {
101 | // This is used to interupt useless callback when the pull up load rolls back.
102 | if ((notification.metrics.outOfRange)) {
103 | return false;
104 | }
105 | GestureProcessor topWrap = _headerKey.currentState as GestureProcessor;
106 | GestureProcessor bottomWrap = _footerKey.currentState as GestureProcessor;
107 | if (widget.enablePullUp) bottomWrap.onDragStart(notification);
108 | if (widget.enablePullDown) topWrap.onDragStart(notification);
109 | return false;
110 | }
111 |
112 | //handle the scrollMoveEvent
113 | bool _handleScrollMoving(ScrollUpdateNotification notification) {
114 | if (_measure(notification) != -1.0)
115 | offsetLis.value = _measure(notification);
116 | GestureProcessor topWrap = _headerKey.currentState as GestureProcessor;
117 | GestureProcessor bottomWrap = _footerKey.currentState as GestureProcessor;
118 | if (widget.enablePullUp) bottomWrap.onDragMove(notification);
119 | if (widget.enablePullDown) topWrap.onDragMove(notification);
120 | return false;
121 | }
122 |
123 | //handle the scrollEndEvent
124 | bool _handleScrollEnd(ScrollNotification notification) {
125 | GestureProcessor topWrap = _headerKey.currentState as GestureProcessor;
126 | GestureProcessor bottomWrap = _footerKey.currentState as GestureProcessor;
127 | if (widget.enablePullUp) bottomWrap.onDragEnd(notification);
128 | if (widget.enablePullDown) topWrap.onDragEnd(notification);
129 | return false;
130 | }
131 |
132 | bool _dispatchScrollEvent(ScrollNotification notification) {
133 | // when is scroll in the ScrollInside,nothing to do
134 | if ((!_isPullUp(notification) && !_isPullDown(notification))) return false;
135 | if (notification is ScrollStartNotification) {
136 | return _handleScrollStart(notification);
137 | }
138 | if (notification is ScrollUpdateNotification) {
139 | //if dragDetails is null,This represents the user's finger out of the screen
140 | if (notification.dragDetails == null) {
141 | return _handleScrollEnd(notification);
142 | } else if (notification.dragDetails != null) {
143 | return _handleScrollMoving(notification);
144 | }
145 | }
146 | if (notification is ScrollEndNotification) {
147 | _handleScrollEnd(notification);
148 | }
149 |
150 | return false;
151 | }
152 |
153 | //check user is pulling up
154 | bool _isPullUp(ScrollNotification noti) {
155 | return noti.metrics.pixels < 0;
156 | }
157 |
158 | //check user is pulling down
159 | bool _isPullDown(ScrollNotification noti) {
160 | return noti.metrics.pixels > 0;
161 | }
162 |
163 | double _measure(ScrollNotification notification) {
164 | if (notification.metrics.minScrollExtent - notification.metrics.pixels >
165 | 0) {
166 | return (notification.metrics.minScrollExtent -
167 | notification.metrics.pixels) /
168 | widget.headerConfig.triggerDistance;
169 | } else if (notification.metrics.pixels -
170 | notification.metrics.maxScrollExtent >
171 | 0) {
172 | return (notification.metrics.pixels -
173 | notification.metrics.maxScrollExtent) /
174 | widget.footerConfig.triggerDistance;
175 | }
176 | return -1.0;
177 | }
178 |
179 | void _init() {
180 | SchedulerBinding.instance.addPostFrameCallback((_) {
181 | _onAfterBuild();
182 | });
183 | _scrollController.addListener(_handleOffsetCallback);
184 | widget.controller._headerMode = topModeLis;
185 | widget.controller._footerMode = bottomModeLis;
186 | }
187 |
188 | void _handleOffsetCallback() {
189 | final double overscrollPastStart = math.max(
190 | _scrollController.position.minScrollExtent -
191 | _scrollController.position.pixels +
192 | (widget.headerConfig is RefreshConfig &&
193 | (topModeLis.value == RefreshStatus.refreshing ||
194 | topModeLis.value == RefreshStatus.completed ||
195 | topModeLis.value == RefreshStatus.failed)
196 | ? (widget.headerConfig as RefreshConfig).visibleRange
197 | : 0.0),
198 | 0.0);
199 | final double overscrollPastEnd = math.max(
200 | _scrollController.position.pixels -
201 | _scrollController.position.maxScrollExtent +
202 | (widget.footerConfig is RefreshConfig &&
203 | (bottomModeLis.value == RefreshStatus.refreshing ||
204 | bottomModeLis.value == RefreshStatus.completed ||
205 | bottomModeLis.value == RefreshStatus.failed)
206 | ? (widget.footerConfig as RefreshConfig).visibleRange
207 | : 0.0),
208 | 0.0);
209 | if (overscrollPastStart > overscrollPastEnd) {
210 | if (widget.headerConfig is RefreshConfig) {
211 | if (widget.onOffsetChange != null) {
212 | widget.onOffsetChange(true, overscrollPastStart);
213 | }
214 | } else {
215 | if (widget.onOffsetChange != null) {
216 | widget.onOffsetChange(true, overscrollPastStart);
217 | }
218 | }
219 | } else if (overscrollPastEnd > 0) {
220 | if (widget.footerConfig is RefreshConfig) {
221 | if (widget.onOffsetChange != null) {
222 | widget.onOffsetChange(false, overscrollPastEnd);
223 | }
224 | } else {
225 | if (widget.onOffsetChange != null) {
226 | widget.onOffsetChange(false, overscrollPastEnd);
227 | }
228 | }
229 | }
230 | }
231 |
232 | _didChangeMode(bool up, ValueNotifier mode) {
233 | switch (mode.value) {
234 | case RefreshStatus.refreshing:
235 | if (widget.onRefresh != null) {
236 | widget.onRefresh(up);
237 | }
238 | if (up && widget.headerConfig is RefreshConfig) {
239 | RefreshConfig config = widget.headerConfig as RefreshConfig;
240 | _scrollController
241 | .jumpTo(_scrollController.offset + config.visibleRange);
242 | }
243 | break;
244 | }
245 | }
246 |
247 | void _onAfterBuild() {
248 | if (widget.headerConfig is LoadConfig) {
249 | if ((widget.headerConfig as LoadConfig).bottomWhenBuild) {
250 | _scrollController.jumpTo(-(_scrollController.position.pixels -
251 | _scrollController.position.maxScrollExtent));
252 | }
253 | }
254 |
255 | topModeLis.addListener(() {
256 | _didChangeMode(true, topModeLis);
257 | });
258 | bottomModeLis.addListener(() {
259 | _didChangeMode(false, bottomModeLis);
260 | });
261 | setState(() {
262 | if (widget.enablePullDown)
263 | _headerHeight = _headerKey.currentContext.size.height;
264 | if (widget.enablePullUp) {
265 | _footerHeight = _footerKey.currentContext.size.height;
266 | }
267 | });
268 | }
269 |
270 | @override
271 | void dispose() {
272 | _scrollController.removeListener(_handleOffsetCallback);
273 | _scrollController.dispose();
274 | super.dispose();
275 | }
276 |
277 | @override
278 | void initState() {
279 | super.initState();
280 | _init();
281 | }
282 |
283 | Widget _buildWrapperByConfig(Config config, bool up) {
284 | if (config is LoadConfig) {
285 | return new LoadWrapper(
286 | key: up ? _headerKey : _footerKey,
287 | modeListener: up ? topModeLis : bottomModeLis,
288 | up: up,
289 | autoLoad: config.autoLoad,
290 | triggerDistance: config.triggerDistance,
291 | builder: up ? widget.headerBuilder : widget.footerBuilder,
292 | );
293 | } else if (config is RefreshConfig) {
294 | return new RefreshWrapper(
295 | key: up ? _headerKey : _footerKey,
296 | modeLis: up ? topModeLis : bottomModeLis,
297 | up: up,
298 | onOffsetChange: (bool up, double offset) {
299 | if (widget.onOffsetChange != null) {
300 | widget.onOffsetChange(
301 | up,
302 | up
303 | ? -_scrollController.offset + offset
304 | : _scrollController.position.pixels -
305 | _scrollController.position.maxScrollExtent +
306 | offset);
307 | }
308 | },
309 | completeDuration: config.completeDuration,
310 | triggerDistance: config.triggerDistance,
311 | visibleRange: config.visibleRange,
312 | builder: up ? widget.headerBuilder : widget.footerBuilder,
313 | );
314 | }
315 | return new Container();
316 | }
317 |
318 | @override
319 | void didUpdateWidget(SmartRefresher oldWidget) {
320 | widget.controller._headerMode = topModeLis;
321 | widget.controller._footerMode = bottomModeLis;
322 | super.didUpdateWidget(oldWidget);
323 | }
324 |
325 | @override
326 | Widget build(BuildContext context) {
327 | final child = widget.child as ListView;
328 | List slivers =
329 | new List.from(child.buildSlivers(context), growable: true);
330 | slivers.add(new SliverToBoxAdapter(
331 | child: widget.footerBuilder != null && widget.enablePullUp
332 | ? _buildWrapperByConfig(widget.footerConfig, false)
333 | : new Container(),
334 | ));
335 | slivers.insert(
336 | 0,
337 | new SliverToBoxAdapter(
338 | child: widget.headerBuilder != null && widget.enablePullDown
339 | ? _buildWrapperByConfig(widget.headerConfig, true)
340 | : new Container()));
341 | return new LayoutBuilder(builder: (context, cons) {
342 | return new Stack(
343 | children: [
344 | new Positioned(
345 | top: !widget.enablePullDown || widget.headerConfig is LoadConfig
346 | ? 0.0
347 | : -_headerHeight,
348 | bottom: !widget.enablePullUp || widget.footerConfig is LoadConfig
349 | ? 0.0
350 | : -_footerHeight,
351 | left: 0.0,
352 | right: 0.0,
353 | child: new NotificationListener(
354 | child: new CustomScrollView(
355 | key: widget.child.key,
356 | physics: new RefreshScrollPhysics(
357 | enableOverScroll: widget.enableOverScroll),
358 | controller: _scrollController,
359 | slivers: slivers,
360 | ),
361 | onNotification: _dispatchScrollEvent,
362 | )),
363 | ],
364 | );
365 | });
366 | }
367 | }
368 |
369 | abstract class Indicator extends StatefulWidget {
370 | final int mode;
371 |
372 | const Indicator({Key key, this.mode}) : super(key: key);
373 | }
374 |
375 | class RefreshController {
376 | ValueNotifier _headerMode;
377 | ValueNotifier _footerMode;
378 | ScrollController scrollController;
379 |
380 | RefreshController() {
381 | scrollController = ScrollController();
382 | }
383 |
384 | void requestRefresh(bool up) {
385 | if (up) {
386 | if (_headerMode.value == RefreshStatus.idle)
387 | _headerMode.value = RefreshStatus.refreshing;
388 | } else {
389 | if (_footerMode.value == RefreshStatus.idle) {
390 | _footerMode.value = RefreshStatus.refreshing;
391 | }
392 | }
393 | }
394 |
395 | void jumpTo(double offset) {
396 | scrollController.jumpTo(offset);
397 | }
398 |
399 | Future animateTo(
400 | double offset, {
401 | @required Duration duration,
402 | @required Curve curve,
403 | }) {
404 | return scrollController.animateTo(offset, duration: duration, curve: curve);
405 | }
406 |
407 | void sendBack(bool up, int mode) {
408 | if (up) {
409 | _headerMode.value = mode;
410 | } else {
411 | _footerMode.value = mode;
412 | }
413 | }
414 |
415 | int get headerMode => _headerMode.value;
416 |
417 | int get footerMode => _footerMode.value;
418 |
419 | isRefresh(bool up) {
420 | if (up) {
421 | return _headerMode.value == RefreshStatus.refreshing;
422 | } else {
423 | return _footerMode.value == RefreshStatus.refreshing;
424 | }
425 | }
426 | }
427 |
--------------------------------------------------------------------------------
/lib/flutter_section_table_view.dart:
--------------------------------------------------------------------------------
1 | library flutter_section_table_view;
2 |
3 | export 'pullrefresh/pull_to_refresh.dart';
4 |
5 | import 'package:flutter/material.dart';
6 | import 'dart:async';
7 | import 'dart:math';
8 | import 'pullrefresh/pull_to_refresh.dart';
9 |
10 | typedef int RowCountInSectionCallBack(int section);
11 | typedef Widget CellAtIndexPathCallBack(int section, int row);
12 | typedef Widget SectionHeaderCallBack(int section);
13 | typedef double SectionHeaderHeightCallBack(int section);
14 | typedef double DividerHeightCallBack();
15 | typedef double CellHeightAtIndexPathCallBack(int section, int row);
16 | typedef void SectionTableViewScrollToCallBack(int section, int row, bool isScrollDown);
17 |
18 | class IndexPath {
19 | final int section;
20 | final int row;
21 | IndexPath({this.section, this.row});
22 | @override
23 | String toString() {
24 | return 'section_${section}_row_$row';
25 | }
26 |
27 | @override
28 | int get hashCode => super.hashCode;
29 | @override
30 | bool operator ==(other) {
31 | if (other.runtimeType != IndexPath) {
32 | return false;
33 | }
34 | IndexPath otherIndexPath = other;
35 | return section == otherIndexPath.section && row == otherIndexPath.row;
36 | }
37 | }
38 |
39 | class SectionTableController extends ChangeNotifier {
40 | IndexPath topIndex = IndexPath(section: 0, row: -1);
41 | bool dirty = false;
42 | bool animate = false;
43 | SectionTableViewScrollToCallBack sectionTableViewScrollTo;
44 |
45 | SectionTableController({this.sectionTableViewScrollTo});
46 |
47 | void jumpTo(int section, int row) {
48 | topIndex = IndexPath(section: section, row: row);
49 | animate = false;
50 | dirty = true;
51 | notifyListeners();
52 | }
53 |
54 | Future animateTo(int section, int row) {
55 | topIndex = IndexPath(section: section, row: row);
56 | animate = true;
57 | dirty = true;
58 | notifyListeners();
59 | return Future.delayed(Duration(milliseconds: 251), () => true);
60 | }
61 | }
62 |
63 | class SectionTableView extends StatefulWidget {
64 | //required
65 | final int sectionCount;
66 | final RowCountInSectionCallBack numOfRowInSection;
67 | final CellAtIndexPathCallBack cellAtIndexPath;
68 |
69 | //section header & divider
70 | final SectionHeaderCallBack headerInSection;
71 | final Widget divider;
72 |
73 | //tell me cell & header & divider height, so that I can scroll to specific index path
74 | //work with SectionTableController
75 | final SectionHeaderHeightCallBack sectionHeaderHeight; // must set when use SectionTableController
76 | final DividerHeightCallBack dividerHeight; // must set when use SectionTableController
77 | final CellHeightAtIndexPathCallBack
78 | cellHeightAtIndexPath; // must set when use SectionTableController
79 | final SectionTableController
80 | controller; //you can use this controller to scroll section table view
81 |
82 | //pull refresh
83 | final IndicatorBuilder
84 | refreshHeaderBuilder; // custom your own refreshHeader, height = 60.0 is better, other value will result in wrong scroll to indexpath offset
85 | final IndicatorBuilder
86 | refreshFooterBuilder; // custom your own refreshFooter, height = 60.0 is better
87 | final Config refreshHeaderConfig, refreshFooterConfig; // configure your refresh header and footer
88 | final bool enablePullUp;
89 | final bool enablePullDown;
90 | final OnRefresh onRefresh;
91 |
92 | final ScrollController _scrollController;
93 | final RefreshController refreshController;
94 | ScrollController get scrollController => _scrollController;
95 |
96 | SectionTableView({
97 | Key key,
98 | @required this.sectionCount,
99 | @required this.numOfRowInSection,
100 | @required this.cellAtIndexPath,
101 | this.headerInSection,
102 | this.divider,
103 | this.sectionHeaderHeight,
104 | this.dividerHeight,
105 | this.cellHeightAtIndexPath,
106 | this.controller,
107 | refreshHeaderBuilder,
108 | refreshFooterBuilder,
109 | this.refreshHeaderConfig: const RefreshConfig(completeDuration: 200),
110 | this.refreshFooterConfig: const RefreshConfig(completeDuration: 200),
111 | this.enablePullDown: false,
112 | this.enablePullUp: false,
113 | this.onRefresh,
114 | this.refreshController,
115 | }) : this.refreshHeaderBuilder = refreshHeaderBuilder ??
116 | ((BuildContext context, int mode) {
117 | return new ClassicIndicator(mode: mode);
118 | }),
119 | this.refreshFooterBuilder = refreshFooterBuilder ??
120 | ((BuildContext context, int mode) {
121 | return new ClassicIndicator(
122 | mode: mode,
123 | releaseText: 'Load when release',
124 | refreshingText: 'Loading...',
125 | completeText: 'Load complete',
126 | failedText: 'Load failed',
127 | idleText: 'Pull up to load more',
128 | idleIcon: const Icon(Icons.arrow_upward, color: Colors.grey),
129 | releaseIcon: const Icon(Icons.arrow_downward, color: Colors.grey),
130 | );
131 | }),
132 | assert((enablePullDown || enablePullUp) ? refreshController != null : true),
133 | _scrollController = (enablePullDown || enablePullUp)
134 | ? refreshController.scrollController
135 | : ScrollController(),
136 | super(key: key);
137 | @override
138 | _SectionTableViewState createState() => new _SectionTableViewState();
139 | }
140 |
141 | class _SectionTableViewState extends State {
142 | List indexToIndexPathSearch = [];
143 | Map indexPathToOffsetSearch;
144 |
145 | final listViewKey = GlobalKey();
146 |
147 | //scroll position check
148 | int currentIndex;
149 | double preIndexOffset;
150 | double nextIndexOffset;
151 |
152 | bool showDivider;
153 |
154 | double scrollOffsetFromIndex(IndexPath indexPath) {
155 | var offset = indexPathToOffsetSearch[indexPath.toString()];
156 | if (offset == null) {
157 | return null;
158 | }
159 | final contentHeight =
160 | indexPathToOffsetSearch[IndexPath(section: widget.sectionCount, row: -1).toString()];
161 |
162 | if (listViewKey.currentContext != null && contentHeight != null) {
163 | double listViewHeight = listViewKey.currentContext.size.height;
164 | if (widget.enablePullUp) {
165 | listViewHeight -= 60.0; //refresh header height
166 | }
167 | if (widget.enablePullDown) {
168 | listViewHeight -= 60.0; //refresh footer height
169 | }
170 | if (offset + listViewHeight > contentHeight) {
171 | // avoid over scroll(bounds)
172 | return max(0.0, contentHeight - listViewHeight);
173 | }
174 | }
175 |
176 | return offset;
177 | }
178 |
179 | void calculateIndexPathAndOffset() {
180 | if (widget.sectionCount == 0) {
181 | return;
182 | }
183 | //calculate index to indexPath mapping
184 | showDivider = false;
185 | bool showSectionHeader = false;
186 | if (widget.divider != null) {
187 | showDivider = true;
188 | }
189 | if (widget.headerInSection != null) {
190 | showSectionHeader = true;
191 | }
192 |
193 | indexToIndexPathSearch = [];
194 | for (int i = 0; i < widget.sectionCount; i++) {
195 | if (showSectionHeader) {
196 | indexToIndexPathSearch.add(IndexPath(section: i, row: -1));
197 | }
198 | int rows = widget.numOfRowInSection(i);
199 | for (int j = 0; j < rows; j++) {
200 | indexToIndexPathSearch.add(IndexPath(section: i, row: j));
201 | }
202 | }
203 |
204 | if (widget.controller == null) {
205 | return;
206 | }
207 |
208 | //only execute below when user want count height and scroll to specific index path
209 | //calculate indexPath to offset mapping
210 | indexPathToOffsetSearch = {};
211 | final sectionController = widget.controller;
212 | if ((showSectionHeader && widget.sectionHeaderHeight == null) ||
213 | (showDivider && widget.dividerHeight == null) ||
214 | widget.cellHeightAtIndexPath == null) {
215 | print(
216 | '''error: if you want to use controller to scroll SectionTableView to wanted index path,
217 | you need to pass parameters:
218 | [sectionHeaderHeight][dividerHeight][cellHeightAtIndexPath]''');
219 | } else {
220 | double offset = 0.0;
221 | double dividerHeight = showDivider ? widget.dividerHeight() : 0.0;
222 | for (int i = 0; i < widget.sectionCount; i++) {
223 | if (showSectionHeader) {
224 | indexPathToOffsetSearch[IndexPath(section: i, row: -1).toString()] = offset;
225 | offset += widget.sectionHeaderHeight(i);
226 | }
227 | int rows = widget.numOfRowInSection(i);
228 | for (int j = 0; j < rows; j++) {
229 | indexPathToOffsetSearch[IndexPath(section: i, row: j).toString()] = offset;
230 | offset += widget.cellHeightAtIndexPath(i, j) + dividerHeight;
231 | }
232 | }
233 | indexPathToOffsetSearch[IndexPath(section: widget.sectionCount, row: -1).toString()] =
234 | offset; //list view length
235 | }
236 |
237 | //calculate initial scroll offset
238 | // double initialOffset = scrollOffsetFromIndex(widget.controller.topIndex);
239 | // if (initialOffset == null) {
240 | // initialOffset = 0.0;
241 | // }
242 |
243 | int findValidIndexPathByIndex(int index, int pace) {
244 | for (int i = index + pace; (i >= 0 && i < indexToIndexPathSearch.length); i += pace) {
245 | final indexPath = indexToIndexPathSearch[i];
246 | if (indexPath.section >= 0) {
247 | return i;
248 | }
249 | }
250 | return index;
251 | }
252 |
253 | if (indexToIndexPathSearch.length == 0) {
254 | return;
255 | }
256 |
257 | if (indexPathToOffsetSearch != null) {
258 | currentIndex = 0;
259 | for (int i = 0; i < indexToIndexPathSearch.length; i++) {
260 | if (indexToIndexPathSearch[i] == sectionController.topIndex) {
261 | currentIndex = i;
262 | }
263 | }
264 |
265 | // final preIndexPath = findValidIndexPathByIndex(currentIndex, -1);
266 | final currentIndexPath = indexToIndexPathSearch[currentIndex];
267 | final nextIndexPath = indexToIndexPathSearch[findValidIndexPathByIndex(currentIndex, 1)];
268 | preIndexOffset = indexPathToOffsetSearch[currentIndexPath.toString()];
269 | nextIndexOffset = indexPathToOffsetSearch[nextIndexPath.toString()];
270 | }
271 |
272 | //init scroll controller
273 | widget.controller.addListener(() {
274 | //listen section table controller to scroll the list view
275 | if (sectionController.dirty) {
276 | sectionController.dirty = false;
277 | double offset = scrollOffsetFromIndex(sectionController.topIndex);
278 | if (offset == null) {
279 | return;
280 | }
281 | if (sectionController.animate) {
282 | widget.scrollController
283 | .animateTo(offset, duration: Duration(milliseconds: 250), curve: Curves.decelerate);
284 | } else {
285 | widget.scrollController.jumpTo(offset);
286 | }
287 | }
288 | });
289 | //listen scroll controller to feedback current index path
290 | if (indexPathToOffsetSearch != null) {
291 | widget.scrollController.addListener(() {
292 | double currentOffset = widget.scrollController.offset;
293 | // print('scroll offset $currentOffset');
294 | if (currentOffset < preIndexOffset) {
295 | //go previous cell
296 | if (currentIndex > 0) {
297 | final nextIndexPath = indexToIndexPathSearch[currentIndex];
298 | currentIndex = findValidIndexPathByIndex(currentIndex, -1);
299 | final currentIndexPath = indexToIndexPathSearch[currentIndex];
300 | preIndexOffset = indexPathToOffsetSearch[currentIndexPath.toString()];
301 | nextIndexOffset = indexPathToOffsetSearch[nextIndexPath.toString()];
302 | // print('go previous index $currentIndexPath');
303 | if (widget.controller.sectionTableViewScrollTo != null) {
304 | widget.controller
305 | .sectionTableViewScrollTo(currentIndexPath.section, currentIndexPath.row, false);
306 | }
307 | }
308 | } else if (currentOffset >= nextIndexOffset) {
309 | //go next cell
310 | if (currentIndex < indexToIndexPathSearch.length - 2) {
311 | currentIndex = findValidIndexPathByIndex(currentIndex, 1);
312 | final currentIndexPath = indexToIndexPathSearch[currentIndex];
313 | final nextIndexPath =
314 | indexToIndexPathSearch[findValidIndexPathByIndex(currentIndex, 1)];
315 | preIndexOffset = indexPathToOffsetSearch[currentIndexPath.toString()];
316 | nextIndexOffset = indexPathToOffsetSearch[nextIndexPath.toString()];
317 | // print('go next index $currentIndexPath');
318 | if (widget.controller.sectionTableViewScrollTo != null) {
319 | widget.controller
320 | .sectionTableViewScrollTo(currentIndexPath.section, currentIndexPath.row, true);
321 | }
322 | }
323 | }
324 | });
325 | }
326 | }
327 |
328 | @override
329 | void initState() {
330 | super.initState();
331 | }
332 |
333 | @override
334 | void dispose() {
335 | super.dispose();
336 | // refreshController.dispose();
337 | // print('SectionTableView dispose');
338 | }
339 |
340 | @override
341 | void didUpdateWidget(SectionTableView oldWidget) {
342 | super.didUpdateWidget(oldWidget);
343 | }
344 |
345 | @override
346 | void didChangeDependencies() {
347 | super.didChangeDependencies();
348 | }
349 |
350 | _buildCell(BuildContext context, int index) {
351 | if (index >= indexToIndexPathSearch.length) {
352 | return null;
353 | }
354 |
355 | IndexPath indexPath = indexToIndexPathSearch[index];
356 | //section header
357 | if (indexPath.section >= 0 && indexPath.row < 0) {
358 | return widget.headerInSection(indexPath.section);
359 | }
360 |
361 | Widget cell = widget.cellAtIndexPath(indexPath.section, indexPath.row);
362 | if (showDivider) {
363 | return Column(
364 | children: [cell, widget.divider],
365 | mainAxisSize: MainAxisSize.min,
366 | );
367 | } else {
368 | return cell;
369 | }
370 | }
371 |
372 | void _onOffsetCallback(bool isUp, double offset) {
373 | // if you want change some widgets state ,you should rewrite the callback
374 | }
375 |
376 | bool usePullRefresh() {
377 | return (widget.enablePullUp || widget.enablePullDown) && widget.refreshController != null;
378 | }
379 |
380 | @override
381 | Widget build(BuildContext context) {
382 | calculateIndexPathAndOffset();
383 | if (usePullRefresh()) {
384 | // print(' use pull refresh');
385 | return SmartRefresher(
386 | headerBuilder: widget.refreshHeaderBuilder,
387 | footerBuilder: widget.refreshFooterBuilder,
388 | headerConfig: widget.refreshHeaderConfig,
389 | footerConfig: widget.refreshFooterConfig,
390 | enablePullDown: widget.enablePullDown,
391 | enablePullUp: widget.enablePullUp,
392 | controller: widget.refreshController,
393 | onRefresh: widget.onRefresh,
394 | onOffsetChange: _onOffsetCallback,
395 | child: ListView.builder(
396 | key: listViewKey,
397 | itemBuilder: (context, index) {
398 | return _buildCell(context, index);
399 | }));
400 | } else {
401 | // print('didn\'t use pull refresh');
402 | return ListView.builder(
403 | key: listViewKey,
404 | physics: AlwaysScrollableScrollPhysics(),
405 | controller: widget.scrollController,
406 | itemBuilder: (context, index) {
407 | return _buildCell(context, index);
408 | });
409 | }
410 | }
411 | }
412 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
12 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
13 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
14 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
15 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
16 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
17 | 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; };
18 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
19 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
20 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
21 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
22 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
23 | /* End PBXBuildFile section */
24 |
25 | /* Begin PBXCopyFilesBuildPhase section */
26 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
27 | isa = PBXCopyFilesBuildPhase;
28 | buildActionMask = 2147483647;
29 | dstPath = "";
30 | dstSubfolderSpec = 10;
31 | files = (
32 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
33 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
34 | );
35 | name = "Embed Frameworks";
36 | runOnlyForDeploymentPostprocessing = 0;
37 | };
38 | /* End PBXCopyFilesBuildPhase section */
39 |
40 | /* Begin PBXFileReference section */
41 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
42 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
43 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
44 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; };
45 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
46 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
47 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
48 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
49 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
50 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; };
51 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
52 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
53 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
54 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
55 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
56 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
57 | /* End PBXFileReference section */
58 |
59 | /* Begin PBXFrameworksBuildPhase section */
60 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
61 | isa = PBXFrameworksBuildPhase;
62 | buildActionMask = 2147483647;
63 | files = (
64 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
65 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
66 | );
67 | runOnlyForDeploymentPostprocessing = 0;
68 | };
69 | /* End PBXFrameworksBuildPhase section */
70 |
71 | /* Begin PBXGroup section */
72 | 9740EEB11CF90186004384FC /* Flutter */ = {
73 | isa = PBXGroup;
74 | children = (
75 | 3B80C3931E831B6300D905FE /* App.framework */,
76 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
77 | 9740EEBA1CF902C7004384FC /* Flutter.framework */,
78 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
79 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
80 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
81 | );
82 | name = Flutter;
83 | sourceTree = "";
84 | };
85 | 97C146E51CF9000F007C117D = {
86 | isa = PBXGroup;
87 | children = (
88 | 9740EEB11CF90186004384FC /* Flutter */,
89 | 97C146F01CF9000F007C117D /* Runner */,
90 | 97C146EF1CF9000F007C117D /* Products */,
91 | CF3B75C9A7D2FA2A4C99F110 /* Frameworks */,
92 | );
93 | sourceTree = "";
94 | };
95 | 97C146EF1CF9000F007C117D /* Products */ = {
96 | isa = PBXGroup;
97 | children = (
98 | 97C146EE1CF9000F007C117D /* Runner.app */,
99 | );
100 | name = Products;
101 | sourceTree = "";
102 | };
103 | 97C146F01CF9000F007C117D /* Runner */ = {
104 | isa = PBXGroup;
105 | children = (
106 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
107 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
108 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
109 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
110 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
111 | 97C147021CF9000F007C117D /* Info.plist */,
112 | 97C146F11CF9000F007C117D /* Supporting Files */,
113 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
114 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
115 | );
116 | path = Runner;
117 | sourceTree = "";
118 | };
119 | 97C146F11CF9000F007C117D /* Supporting Files */ = {
120 | isa = PBXGroup;
121 | children = (
122 | 97C146F21CF9000F007C117D /* main.m */,
123 | );
124 | name = "Supporting Files";
125 | sourceTree = "";
126 | };
127 | /* End PBXGroup section */
128 |
129 | /* Begin PBXNativeTarget section */
130 | 97C146ED1CF9000F007C117D /* Runner */ = {
131 | isa = PBXNativeTarget;
132 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
133 | buildPhases = (
134 | 9740EEB61CF901F6004384FC /* Run Script */,
135 | 97C146EA1CF9000F007C117D /* Sources */,
136 | 97C146EB1CF9000F007C117D /* Frameworks */,
137 | 97C146EC1CF9000F007C117D /* Resources */,
138 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
139 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
140 | );
141 | buildRules = (
142 | );
143 | dependencies = (
144 | );
145 | name = Runner;
146 | productName = Runner;
147 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
148 | productType = "com.apple.product-type.application";
149 | };
150 | /* End PBXNativeTarget section */
151 |
152 | /* Begin PBXProject section */
153 | 97C146E61CF9000F007C117D /* Project object */ = {
154 | isa = PBXProject;
155 | attributes = {
156 | LastUpgradeCheck = 0910;
157 | ORGANIZATIONNAME = "The Chromium Authors";
158 | TargetAttributes = {
159 | 97C146ED1CF9000F007C117D = {
160 | CreatedOnToolsVersion = 7.3.1;
161 | };
162 | };
163 | };
164 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
165 | compatibilityVersion = "Xcode 3.2";
166 | developmentRegion = English;
167 | hasScannedForEncodings = 0;
168 | knownRegions = (
169 | en,
170 | Base,
171 | );
172 | mainGroup = 97C146E51CF9000F007C117D;
173 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
174 | projectDirPath = "";
175 | projectRoot = "";
176 | targets = (
177 | 97C146ED1CF9000F007C117D /* Runner */,
178 | );
179 | };
180 | /* End PBXProject section */
181 |
182 | /* Begin PBXResourcesBuildPhase section */
183 | 97C146EC1CF9000F007C117D /* Resources */ = {
184 | isa = PBXResourcesBuildPhase;
185 | buildActionMask = 2147483647;
186 | files = (
187 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
188 | 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */,
189 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
190 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
191 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
192 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
193 | );
194 | runOnlyForDeploymentPostprocessing = 0;
195 | };
196 | /* End PBXResourcesBuildPhase section */
197 |
198 | /* Begin PBXShellScriptBuildPhase section */
199 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
200 | isa = PBXShellScriptBuildPhase;
201 | buildActionMask = 2147483647;
202 | files = (
203 | );
204 | inputPaths = (
205 | );
206 | name = "Thin Binary";
207 | outputPaths = (
208 | );
209 | runOnlyForDeploymentPostprocessing = 0;
210 | shellPath = /bin/sh;
211 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
212 | };
213 | 9740EEB61CF901F6004384FC /* Run Script */ = {
214 | isa = PBXShellScriptBuildPhase;
215 | buildActionMask = 2147483647;
216 | files = (
217 | );
218 | inputPaths = (
219 | );
220 | name = "Run Script";
221 | outputPaths = (
222 | );
223 | runOnlyForDeploymentPostprocessing = 0;
224 | shellPath = /bin/sh;
225 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
226 | };
227 | /* End PBXShellScriptBuildPhase section */
228 |
229 | /* Begin PBXSourcesBuildPhase section */
230 | 97C146EA1CF9000F007C117D /* Sources */ = {
231 | isa = PBXSourcesBuildPhase;
232 | buildActionMask = 2147483647;
233 | files = (
234 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
235 | 97C146F31CF9000F007C117D /* main.m in Sources */,
236 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
237 | );
238 | runOnlyForDeploymentPostprocessing = 0;
239 | };
240 | /* End PBXSourcesBuildPhase section */
241 |
242 | /* Begin PBXVariantGroup section */
243 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
244 | isa = PBXVariantGroup;
245 | children = (
246 | 97C146FB1CF9000F007C117D /* Base */,
247 | );
248 | name = Main.storyboard;
249 | sourceTree = "";
250 | };
251 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
252 | isa = PBXVariantGroup;
253 | children = (
254 | 97C147001CF9000F007C117D /* Base */,
255 | );
256 | name = LaunchScreen.storyboard;
257 | sourceTree = "";
258 | };
259 | /* End PBXVariantGroup section */
260 |
261 | /* Begin XCBuildConfiguration section */
262 | 97C147031CF9000F007C117D /* Debug */ = {
263 | isa = XCBuildConfiguration;
264 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
265 | buildSettings = {
266 | ALWAYS_SEARCH_USER_PATHS = NO;
267 | CLANG_ANALYZER_NONNULL = YES;
268 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
269 | CLANG_CXX_LIBRARY = "libc++";
270 | CLANG_ENABLE_MODULES = YES;
271 | CLANG_ENABLE_OBJC_ARC = YES;
272 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
273 | CLANG_WARN_BOOL_CONVERSION = YES;
274 | CLANG_WARN_COMMA = YES;
275 | CLANG_WARN_CONSTANT_CONVERSION = YES;
276 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
277 | CLANG_WARN_EMPTY_BODY = YES;
278 | CLANG_WARN_ENUM_CONVERSION = YES;
279 | CLANG_WARN_INFINITE_RECURSION = YES;
280 | CLANG_WARN_INT_CONVERSION = YES;
281 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
282 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
283 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
284 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
285 | CLANG_WARN_STRICT_PROTOTYPES = YES;
286 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
287 | CLANG_WARN_UNREACHABLE_CODE = YES;
288 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
289 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
290 | COPY_PHASE_STRIP = NO;
291 | DEBUG_INFORMATION_FORMAT = dwarf;
292 | ENABLE_STRICT_OBJC_MSGSEND = YES;
293 | ENABLE_TESTABILITY = YES;
294 | GCC_C_LANGUAGE_STANDARD = gnu99;
295 | GCC_DYNAMIC_NO_PIC = NO;
296 | GCC_NO_COMMON_BLOCKS = YES;
297 | GCC_OPTIMIZATION_LEVEL = 0;
298 | GCC_PREPROCESSOR_DEFINITIONS = (
299 | "DEBUG=1",
300 | "$(inherited)",
301 | );
302 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
303 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
304 | GCC_WARN_UNDECLARED_SELECTOR = YES;
305 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
306 | GCC_WARN_UNUSED_FUNCTION = YES;
307 | GCC_WARN_UNUSED_VARIABLE = YES;
308 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
309 | MTL_ENABLE_DEBUG_INFO = YES;
310 | ONLY_ACTIVE_ARCH = YES;
311 | SDKROOT = iphoneos;
312 | TARGETED_DEVICE_FAMILY = "1,2";
313 | };
314 | name = Debug;
315 | };
316 | 97C147041CF9000F007C117D /* Release */ = {
317 | isa = XCBuildConfiguration;
318 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
319 | buildSettings = {
320 | ALWAYS_SEARCH_USER_PATHS = NO;
321 | CLANG_ANALYZER_NONNULL = YES;
322 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
323 | CLANG_CXX_LIBRARY = "libc++";
324 | CLANG_ENABLE_MODULES = YES;
325 | CLANG_ENABLE_OBJC_ARC = YES;
326 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
327 | CLANG_WARN_BOOL_CONVERSION = YES;
328 | CLANG_WARN_COMMA = YES;
329 | CLANG_WARN_CONSTANT_CONVERSION = YES;
330 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
331 | CLANG_WARN_EMPTY_BODY = YES;
332 | CLANG_WARN_ENUM_CONVERSION = YES;
333 | CLANG_WARN_INFINITE_RECURSION = YES;
334 | CLANG_WARN_INT_CONVERSION = YES;
335 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
336 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
337 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
338 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
339 | CLANG_WARN_STRICT_PROTOTYPES = YES;
340 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
341 | CLANG_WARN_UNREACHABLE_CODE = YES;
342 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
343 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
344 | COPY_PHASE_STRIP = NO;
345 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
346 | ENABLE_NS_ASSERTIONS = NO;
347 | ENABLE_STRICT_OBJC_MSGSEND = YES;
348 | GCC_C_LANGUAGE_STANDARD = gnu99;
349 | GCC_NO_COMMON_BLOCKS = YES;
350 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
351 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
352 | GCC_WARN_UNDECLARED_SELECTOR = YES;
353 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
354 | GCC_WARN_UNUSED_FUNCTION = YES;
355 | GCC_WARN_UNUSED_VARIABLE = YES;
356 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
357 | MTL_ENABLE_DEBUG_INFO = NO;
358 | SDKROOT = iphoneos;
359 | TARGETED_DEVICE_FAMILY = "1,2";
360 | VALIDATE_PRODUCT = YES;
361 | };
362 | name = Release;
363 | };
364 | 97C147061CF9000F007C117D /* Debug */ = {
365 | isa = XCBuildConfiguration;
366 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
367 | buildSettings = {
368 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
369 | CURRENT_PROJECT_VERSION = 1;
370 | ENABLE_BITCODE = NO;
371 | FRAMEWORK_SEARCH_PATHS = (
372 | "$(inherited)",
373 | "$(PROJECT_DIR)/Flutter",
374 | );
375 | INFOPLIST_FILE = Runner/Info.plist;
376 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
377 | LIBRARY_SEARCH_PATHS = (
378 | "$(inherited)",
379 | "$(PROJECT_DIR)/Flutter",
380 | );
381 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
382 | PRODUCT_NAME = "$(TARGET_NAME)";
383 | VERSIONING_SYSTEM = "apple-generic";
384 | };
385 | name = Debug;
386 | };
387 | 97C147071CF9000F007C117D /* Release */ = {
388 | isa = XCBuildConfiguration;
389 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
390 | buildSettings = {
391 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
392 | CURRENT_PROJECT_VERSION = 1;
393 | ENABLE_BITCODE = NO;
394 | FRAMEWORK_SEARCH_PATHS = (
395 | "$(inherited)",
396 | "$(PROJECT_DIR)/Flutter",
397 | );
398 | INFOPLIST_FILE = Runner/Info.plist;
399 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
400 | LIBRARY_SEARCH_PATHS = (
401 | "$(inherited)",
402 | "$(PROJECT_DIR)/Flutter",
403 | );
404 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
405 | PRODUCT_NAME = "$(TARGET_NAME)";
406 | VERSIONING_SYSTEM = "apple-generic";
407 | };
408 | name = Release;
409 | };
410 | /* End XCBuildConfiguration section */
411 |
412 | /* Begin XCConfigurationList section */
413 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
414 | isa = XCConfigurationList;
415 | buildConfigurations = (
416 | 97C147031CF9000F007C117D /* Debug */,
417 | 97C147041CF9000F007C117D /* Release */,
418 | );
419 | defaultConfigurationIsVisible = 0;
420 | defaultConfigurationName = Release;
421 | };
422 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
423 | isa = XCConfigurationList;
424 | buildConfigurations = (
425 | 97C147061CF9000F007C117D /* Debug */,
426 | 97C147071CF9000F007C117D /* Release */,
427 | );
428 | defaultConfigurationIsVisible = 0;
429 | defaultConfigurationName = Release;
430 | };
431 | /* End XCConfigurationList section */
432 | };
433 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
434 | }
435 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
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 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | startIndex
146 | scrollOffsetFromIndex
147 | addlis
148 | scrollController
149 | controller
150 | widget.controller
151 | initialOffset
152 | completed
153 | findValidIndexPathByIndex
154 | refreshController
155 | sectionTableController
156 | listview
157 | child
158 | _headerHeight
159 | is
160 | listViewKey
161 | didn't use
162 | received scroll to
163 | didn't use pull refresh
164 | refreshHeaderBuilder
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 | 1534227416466
412 |
413 |
414 | 1534227416466
415 |
416 |
417 | 1534229356315
418 |
419 |
420 |
421 | 1534229356315
422 |
423 |
424 | 1534234446418
425 |
426 |
427 |
428 | 1534234446418
429 |
430 |
431 | 1534296363063
432 |
433 |
434 |
435 | 1534296363063
436 |
437 |
438 | 1534296511667
439 |
440 |
441 |
442 | 1534296511667
443 |
444 |
445 | 1534298758657
446 |
447 |
448 |
449 | 1534298758657
450 |
451 |
452 | 1534299465003
453 |
454 |
455 |
456 | 1534299465003
457 |
458 |
459 | 1534388672882
460 |
461 |
462 |
463 | 1534388672882
464 |
465 |
466 | 1534406622380
467 |
468 |
469 |
470 | 1534406622380
471 |
472 |
473 | 1534408835230
474 |
475 |
476 |
477 | 1534408835230
478 |
479 |
480 | 1534408958412
481 |
482 |
483 |
484 | 1534408958412
485 |
486 |
487 | 1534410378275
488 |
489 |
490 |
491 | 1534410378275
492 |
493 |
494 | 1534412741822
495 |
496 |
497 |
498 | 1534412741822
499 |
500 |
501 | 1534990813222
502 |
503 |
504 |
505 | 1534990813222
506 |
507 |
508 | 1535514426085
509 |
510 |
511 |
512 | 1535514426085
513 |
514 |
515 | 1535517542854
516 |
517 |
518 |
519 | 1535517542854
520 |
521 |
522 | 1535518176735
523 |
524 |
525 |
526 | 1535518176735
527 |
528 |
529 | 1535518673604
530 |
531 |
532 |
533 | 1535518673604
534 |
535 |
536 | 1535520796168
537 |
538 |
539 |
540 | 1535520796168
541 |
542 |
543 | 1536049475128
544 |
545 |
546 |
547 | 1536049475128
548 |
549 |
550 | 1536054500443
551 |
552 |
553 |
554 | 1536054500443
555 |
556 |
557 | 1538101067162
558 |
559 |
560 |
561 | 1538101067162
562 |
563 |
564 | 1538101077217
565 |
566 |
567 |
568 | 1538101077217
569 |
570 |
571 | 1538101167963
572 |
573 |
574 |
575 | 1538101167963
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 | file://$USER_HOME$/.pub-cache/git/flutter_pulltorefresh-13a954b83034d17ffb7114b79b0b889e9792f8ee/lib/pull_to_refresh.dart
660 | 8
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
747 |
748 |
749 |
750 |
751 |
752 |
753 |
754 |
755 |
756 |
757 |
758 |
759 |
760 |
761 |
762 |
763 |
764 |
765 |
766 |
767 |
768 |
769 |
770 |
771 |
772 |
773 |
774 |
775 |
776 |
777 |
778 |
779 |
780 |
781 |
782 |
783 |
784 |
785 |
786 |
787 |
788 |
789 |
790 |
791 |
792 |
793 |
794 |
795 |
796 |
797 |
798 |
799 |
800 |
801 |
802 |
803 |
804 |
805 |
806 |
807 |
808 |
809 |
810 |
811 |
812 |
813 |
814 |
815 |
816 |
817 |
818 |
819 |
820 |
821 |
822 |
823 |
824 |
825 |
826 |
827 |
828 |
829 |
830 |
831 |
832 |
833 |
834 |
835 |
836 |
837 |
838 |
839 |
840 |
841 |
842 |
843 |
844 |
845 |
846 |
847 |
848 |
849 |
850 |
851 |
852 |
853 |
854 |
855 |
856 |
857 |
858 |
859 |
860 |
861 |
862 |
863 |
864 |
865 |
866 |
867 |
868 |
869 |
870 |
871 |
872 |
873 |
874 |
875 |
876 |
877 |
878 |
879 |
880 |
881 |
882 |
883 |
884 |
885 |
886 |
887 |
888 |
889 |
890 |
891 |
892 |
893 |
894 |
895 |
896 |
897 |
898 |
899 |
900 |
901 |
902 |
903 |
904 |
905 |
906 |
907 |
908 |
909 |
910 |
911 |
912 |
913 |
914 |
915 |
916 |
917 |
918 |
919 |
920 |
921 |
922 |
923 |
924 |
925 |
926 |
927 |
928 |
929 |
930 |
931 |
932 |
933 |
934 |
935 |
936 |
937 |
938 |
939 |
940 |
941 |
942 |
943 |
944 |
945 |
946 |
947 |
948 |
949 |
950 |
951 |
952 |
953 |
954 |
955 |
956 |
957 |
958 |
959 |
960 |
961 |
962 |
963 |
964 |
965 |
966 |
967 |
968 |
969 |
970 |
971 |
972 |
973 |
974 |
975 |
976 |
977 |
978 |
979 |
980 |
981 |
982 |
983 |
984 |
985 |
986 |
--------------------------------------------------------------------------------