From 3e5c11a461afb899b38cf153b9027d1c1a0a3822 Mon Sep 17 00:00:00 2001 From: Olekandr Kirichenko Date: Fri, 6 Aug 2021 16:40:24 +0200 Subject: [PATCH] null safety support --- .gitignore | 118 +++++++++++++- CHANGELOG.md | 3 +- example/ios/Runner.xcodeproj/project.pbxproj | 16 +- .../contents.xcworkspacedata | 2 +- .../xcshareddata/WorkspaceSettings.xcsettings | 8 - example/lib/FullList.dart | 1 + example/pubspec.lock | 153 ++++++++++++++++++ example/pubspec.yaml | 6 +- example/test/widget_test.dart | 2 +- lib/flutter_section_table_view.dart | 114 ++++++------- .../src/indicator/classic_indicator.dart | 4 +- .../src/internals/indicator_config.dart | 2 +- .../src/internals/indicator_wrap.dart | 38 ++--- .../src/internals/refresh_physics.dart | 6 +- lib/pullrefresh/src/smart_refresher.dart | 87 +++++----- pubspec.lock | 146 +++++++++++++++++ pubspec.yaml | 4 +- 17 files changed, 549 insertions(+), 161 deletions(-) delete mode 100644 example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 example/pubspec.lock create mode 100644 pubspec.lock diff --git a/.gitignore b/.gitignore index 7bf00e8..d404db1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,117 @@ -# See https://www.dartlang.org/guides/libraries/private-files +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ -# Files and directories created by pub +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# Visual Studio Code related +.classpath +.project +.settings/ +#.vscode/ + +# Flutter repo-specific +/bin/cache/ +/bin/mingit/ +/dev/benchmarks/mega_gallery/ +/dev/bots/.recipe_deps +/dev/bots/android_tools/ +/dev/devicelab/ABresults*.json +/dev/docs/doc/ +/dev/docs/flutter.docs.zip +/dev/docs/lib/ +/dev/docs/pubspec.yaml +/dev/integration_tests/**/xcuserdata +/dev/integration_tests/**/Pods +/packages/flutter/coverage/ +version +analysis_benchmark.json + +# packages file containing multi-root paths +.packages.generated + +# Flutter/Dart/Pub related +**/doc/api/ .dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +**/generated_plugin_registrant.dart .packages +.pub-cache/ .pub/ build/ -# If you're building an application, you may want to check-in your pubspec.lock -pubspec.lock +flutter_*.png +linked_*.ds +unlinked.ds +unlinked_spec.ds + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java +**/android/key.properties +*.jks + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/.last_build_id +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# macOS +**/macos/Flutter/GeneratedPluginRegistrant.swift +**/macos/Flutter/Flutter-Debug.xcconfig +**/macos/Flutter/Flutter-Release.xcconfig +**/macos/Flutter/Flutter-Profile.xcconfig + +# Coverage +coverage/ + +# Symbols +app.*.symbols -# Directory created by dartdoc -# If you don't generate documentation locally you can remove this line. -doc/api/ +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages +!/dev/ci/**/Gemfile.lock \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ce1900..9a5c995 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,4 +15,5 @@ ## [1.0.0] - release stable version ## [1.0.1] - add always scroll physics ## [1.0.2] - re-format -## [1.0.3] - fix bug for empty list \ No newline at end of file +## [1.0.3] - fix bug for empty list +## [1.0.4] - null safety \ No newline at end of file diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index a9aca1e..b184072 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,10 +9,6 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; @@ -29,8 +25,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -41,13 +35,11 @@ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -61,8 +53,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -72,9 +62,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -208,7 +196,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; @@ -261,7 +249,6 @@ /* Begin XCBuildConfiguration section */ 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -315,7 +302,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 1d526a1..919434a 100644 --- a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index 949b678..0000000 --- a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - BuildSystemType - Original - - diff --git a/example/lib/FullList.dart b/example/lib/FullList.dart index 457400c..f6add14 100644 --- a/example/lib/FullList.dart +++ b/example/lib/FullList.dart @@ -79,6 +79,7 @@ class _FullListState extends State { Future.delayed(const Duration(milliseconds: 2009)).then((val) { refreshController.sendBack(up, RefreshStatus.completed); + if (!mounted) return; setState(() { if (up) { sectionCount = 4; diff --git a/example/pubspec.lock b/example/pubspec.lock new file mode 100644 index 0000000..639ebb8 --- /dev/null +++ b/example/pubspec.lock @@ -0,0 +1,153 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.7.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_section_table_view: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "1.0.4" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" +sdks: + dart: ">=2.13.0 <3.0.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 0e3887f..76c4452 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,6 +1,10 @@ name: example +version: 1.0.0 +publish_to: 'none' description: A new Flutter project. - +environment: + sdk: ">=2.13.0 <3.0.0" + dependencies: flutter: sdk: flutter diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart index 63d397c..2c2b24a 100644 --- a/example/test/widget_test.dart +++ b/example/test/widget_test.dart @@ -7,7 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:example/main.dart'; +import '../lib/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { diff --git a/lib/flutter_section_table_view.dart b/lib/flutter_section_table_view.dart index a465763..95e3633 100644 --- a/lib/flutter_section_table_view.dart +++ b/lib/flutter_section_table_view.dart @@ -18,7 +18,7 @@ typedef void SectionTableViewScrollToCallBack(int section, int row, bool isScrol class IndexPath { final int section; final int row; - IndexPath({this.section, this.row}); + IndexPath({required this.section, required this.row}); @override String toString() { return 'section_${section}_row_$row'; @@ -28,11 +28,9 @@ class IndexPath { int get hashCode => super.hashCode; @override bool operator ==(other) { - if (other.runtimeType != IndexPath) { - return false; - } - IndexPath otherIndexPath = other; - return section == otherIndexPath.section && row == otherIndexPath.row; + if (other is IndexPath) { + return section == other.section && row == other.row; + } else { return false; } } } @@ -40,7 +38,7 @@ class SectionTableController extends ChangeNotifier { IndexPath topIndex = IndexPath(section: 0, row: -1); bool dirty = false; bool animate = false; - SectionTableViewScrollToCallBack sectionTableViewScrollTo; + SectionTableViewScrollToCallBack? sectionTableViewScrollTo; SectionTableController({this.sectionTableViewScrollTo}); @@ -67,16 +65,16 @@ class SectionTableView extends StatefulWidget { final CellAtIndexPathCallBack cellAtIndexPath; //section header & divider - final SectionHeaderCallBack headerInSection; - final Widget divider; + final SectionHeaderCallBack? headerInSection; + final Widget? divider; //tell me cell & header & divider height, so that I can scroll to specific index path //work with SectionTableController - final SectionHeaderHeightCallBack sectionHeaderHeight; // must set when use SectionTableController - final DividerHeightCallBack dividerHeight; // must set when use SectionTableController - final CellHeightAtIndexPathCallBack + final SectionHeaderHeightCallBack? sectionHeaderHeight; // must set when use SectionTableController + final DividerHeightCallBack? dividerHeight; // must set when use SectionTableController + final CellHeightAtIndexPathCallBack? cellHeightAtIndexPath; // must set when use SectionTableController - final SectionTableController + final SectionTableController? controller; //you can use this controller to scroll section table view //pull refresh @@ -87,17 +85,17 @@ class SectionTableView extends StatefulWidget { final Config refreshHeaderConfig, refreshFooterConfig; // configure your refresh header and footer final bool enablePullUp; final bool enablePullDown; - final OnRefresh onRefresh; + final OnRefresh? onRefresh; final ScrollController _scrollController; - final RefreshController refreshController; + final RefreshController? refreshController; ScrollController get scrollController => _scrollController; SectionTableView({ - Key key, - @required this.sectionCount, - @required this.numOfRowInSection, - @required this.cellAtIndexPath, + Key? key, + required this.sectionCount, + required this.numOfRowInSection, + required this.cellAtIndexPath, this.headerInSection, this.divider, this.sectionHeaderHeight, @@ -131,7 +129,7 @@ class SectionTableView extends StatefulWidget { }), assert((enablePullDown || enablePullUp) ? refreshController != null : true), _scrollController = (enablePullDown || enablePullUp) - ? refreshController.scrollController + ? refreshController!.scrollController : ScrollController(), super(key: key); @override @@ -140,27 +138,27 @@ class SectionTableView extends StatefulWidget { class _SectionTableViewState extends State { List indexToIndexPathSearch = []; - Map indexPathToOffsetSearch; + Map? indexPathToOffsetSearch; final listViewKey = GlobalKey(); //scroll position check - int currentIndex; - double preIndexOffset; - double nextIndexOffset; + int currentIndex = 0; + double preIndexOffset = 0; + double nextIndexOffset = 0; - bool showDivider; + bool showDivider = false; - double scrollOffsetFromIndex(IndexPath indexPath) { - var offset = indexPathToOffsetSearch[indexPath.toString()]; + double? scrollOffsetFromIndex(IndexPath indexPath) { + var offset = indexPathToOffsetSearch?[indexPath.toString()]; if (offset == null) { return null; } final contentHeight = - indexPathToOffsetSearch[IndexPath(section: widget.sectionCount, row: -1).toString()]; + indexPathToOffsetSearch?[IndexPath(section: widget.sectionCount, row: -1).toString()]; if (listViewKey.currentContext != null && contentHeight != null) { - double listViewHeight = listViewKey.currentContext.size.height; + var listViewHeight = listViewKey.currentContext!.size!.height; if (widget.enablePullUp) { listViewHeight -= 60.0; //refresh header height } @@ -204,11 +202,11 @@ class _SectionTableViewState extends State { if (widget.controller == null) { return; } + final sectionController = widget.controller!; //only execute below when user want count height and scroll to specific index path //calculate indexPath to offset mapping indexPathToOffsetSearch = {}; - final sectionController = widget.controller; if ((showSectionHeader && widget.sectionHeaderHeight == null) || (showDivider && widget.dividerHeight == null) || widget.cellHeightAtIndexPath == null) { @@ -218,19 +216,19 @@ class _SectionTableViewState extends State { [sectionHeaderHeight][dividerHeight][cellHeightAtIndexPath]'''); } else { double offset = 0.0; - double dividerHeight = showDivider ? widget.dividerHeight() : 0.0; + double dividerHeight = showDivider && widget.dividerHeight != null ? widget.dividerHeight!() : 0.0; for (int i = 0; i < widget.sectionCount; i++) { - if (showSectionHeader) { - indexPathToOffsetSearch[IndexPath(section: i, row: -1).toString()] = offset; - offset += widget.sectionHeaderHeight(i); + if (showSectionHeader && widget.sectionHeaderHeight != null) { + indexPathToOffsetSearch?[IndexPath(section: i, row: -1).toString()] = offset; + offset += widget.sectionHeaderHeight!(i); } int rows = widget.numOfRowInSection(i); for (int j = 0; j < rows; j++) { - indexPathToOffsetSearch[IndexPath(section: i, row: j).toString()] = offset; - offset += widget.cellHeightAtIndexPath(i, j) + dividerHeight; + indexPathToOffsetSearch?[IndexPath(section: i, row: j).toString()] = offset; + offset += widget.cellHeightAtIndexPath!(i, j) + dividerHeight; } } - indexPathToOffsetSearch[IndexPath(section: widget.sectionCount, row: -1).toString()] = + indexPathToOffsetSearch?[IndexPath(section: widget.sectionCount, row: -1).toString()] = offset; //list view length } @@ -265,16 +263,16 @@ class _SectionTableViewState extends State { // final preIndexPath = findValidIndexPathByIndex(currentIndex, -1); final currentIndexPath = indexToIndexPathSearch[currentIndex]; final nextIndexPath = indexToIndexPathSearch[findValidIndexPathByIndex(currentIndex, 1)]; - preIndexOffset = indexPathToOffsetSearch[currentIndexPath.toString()]; - nextIndexOffset = indexPathToOffsetSearch[nextIndexPath.toString()]; + preIndexOffset = indexPathToOffsetSearch![currentIndexPath.toString()] ?? 0; + nextIndexOffset = indexPathToOffsetSearch![nextIndexPath.toString()] ?? 0; } //init scroll controller - widget.controller.addListener(() { + widget.controller?.addListener(() { //listen section table controller to scroll the list view if (sectionController.dirty) { sectionController.dirty = false; - double offset = scrollOffsetFromIndex(sectionController.topIndex); + double? offset = scrollOffsetFromIndex(sectionController.topIndex); if (offset == null) { return; } @@ -297,12 +295,12 @@ class _SectionTableViewState extends State { final nextIndexPath = indexToIndexPathSearch[currentIndex]; currentIndex = findValidIndexPathByIndex(currentIndex, -1); final currentIndexPath = indexToIndexPathSearch[currentIndex]; - preIndexOffset = indexPathToOffsetSearch[currentIndexPath.toString()]; - nextIndexOffset = indexPathToOffsetSearch[nextIndexPath.toString()]; + preIndexOffset = indexPathToOffsetSearch![currentIndexPath.toString()] ?? 0; + nextIndexOffset = indexPathToOffsetSearch![nextIndexPath.toString()] ?? 0; // print('go previous index $currentIndexPath'); - if (widget.controller.sectionTableViewScrollTo != null) { - widget.controller - .sectionTableViewScrollTo(currentIndexPath.section, currentIndexPath.row, false); + if (widget.controller?.sectionTableViewScrollTo != null) { + widget.controller! + .sectionTableViewScrollTo!(currentIndexPath.section, currentIndexPath.row, false); } } } else if (currentOffset >= nextIndexOffset) { @@ -312,12 +310,12 @@ class _SectionTableViewState extends State { final currentIndexPath = indexToIndexPathSearch[currentIndex]; final nextIndexPath = indexToIndexPathSearch[findValidIndexPathByIndex(currentIndex, 1)]; - preIndexOffset = indexPathToOffsetSearch[currentIndexPath.toString()]; - nextIndexOffset = indexPathToOffsetSearch[nextIndexPath.toString()]; + preIndexOffset = indexPathToOffsetSearch![currentIndexPath.toString()] ?? 0; + nextIndexOffset = indexPathToOffsetSearch![nextIndexPath.toString()] ?? 0; // print('go next index $currentIndexPath'); - if (widget.controller.sectionTableViewScrollTo != null) { - widget.controller - .sectionTableViewScrollTo(currentIndexPath.section, currentIndexPath.row, true); + if (widget.controller?.sectionTableViewScrollTo != null) { + widget.controller! + .sectionTableViewScrollTo!(currentIndexPath.section, currentIndexPath.row, true); } } } @@ -347,21 +345,23 @@ class _SectionTableViewState extends State { super.didChangeDependencies(); } - _buildCell(BuildContext context, int index) { + Widget _buildCell(BuildContext context, int index) { if (index >= indexToIndexPathSearch.length) { - return null; + return Container(); } IndexPath indexPath = indexToIndexPathSearch[index]; //section header if (indexPath.section >= 0 && indexPath.row < 0) { - return widget.headerInSection(indexPath.section); + if (widget.headerInSection != null) { + return widget.headerInSection!(indexPath.section); + } else { return Container(); } } Widget cell = widget.cellAtIndexPath(indexPath.section, indexPath.row); - if (showDivider) { + if (showDivider && widget.divider != null) { return Column( - children: [cell, widget.divider], + children: [cell, widget.divider!], mainAxisSize: MainAxisSize.min, ); } else { @@ -394,6 +394,7 @@ class _SectionTableViewState extends State { onOffsetChange: _onOffsetCallback, child: ListView.builder( key: listViewKey, + itemCount: indexToIndexPathSearch.length, itemBuilder: (context, index) { return _buildCell(context, index); })); @@ -403,6 +404,7 @@ class _SectionTableViewState extends State { key: listViewKey, physics: AlwaysScrollableScrollPhysics(), controller: widget.scrollController, + itemCount: indexToIndexPathSearch.length, itemBuilder: (context, index) { return _buildCell(context, index); }); diff --git a/lib/pullrefresh/src/indicator/classic_indicator.dart b/lib/pullrefresh/src/indicator/classic_indicator.dart index c3989b8..759c38e 100644 --- a/lib/pullrefresh/src/indicator/classic_indicator.dart +++ b/lib/pullrefresh/src/indicator/classic_indicator.dart @@ -34,8 +34,8 @@ class ClassicIndicator extends Indicator { final TextStyle textStyle; const ClassicIndicator({ - @required int mode, - Key key, + required int mode, + Key? key, this.textStyle: const TextStyle(color: const Color(0xff555555)), this.releaseText: 'Refresh when release', this.refreshingText: 'Refreshing...', diff --git a/lib/pullrefresh/src/internals/indicator_config.dart b/lib/pullrefresh/src/internals/indicator_config.dart index 6e9cd05..f0f87dd 100644 --- a/lib/pullrefresh/src/internals/indicator_config.dart +++ b/lib/pullrefresh/src/internals/indicator_config.dart @@ -18,7 +18,7 @@ abstract class Config { // How many distances should be dragged to trigger refresh final double triggerDistance; - const Config({this.triggerDistance}); + const Config({required this.triggerDistance}); } class RefreshConfig extends Config { diff --git a/lib/pullrefresh/src/internals/indicator_wrap.dart b/lib/pullrefresh/src/internals/indicator_wrap.dart index df4da0c..319a2cb 100644 --- a/lib/pullrefresh/src/internals/indicator_wrap.dart +++ b/lib/pullrefresh/src/internals/indicator_wrap.dart @@ -31,11 +31,11 @@ abstract class Wrapper extends StatefulWidget { set mode(int mode) => this.modeListener.value = mode; Wrapper( - {Key key, - @required this.up, - @required this.modeListener, - this.builder, - this.triggerDistance}) + {Key? key, + required this.up, + required this.modeListener, + required this.builder, + required this.triggerDistance}) : assert(up != null, modeListener != null), super(key: key); @@ -59,17 +59,17 @@ abstract class Wrapper extends StatefulWidget { class RefreshWrapper extends Wrapper { final int completeDuration; - final Function onOffsetChange; + final Function? onOffsetChange; final double visibleRange; RefreshWrapper({ - Key key, - IndicatorBuilder builder, - ValueNotifier modeLis, + Key? key, + required IndicatorBuilder builder, + required ValueNotifier modeLis, this.onOffsetChange, this.completeDuration: default_completeDuration, - double triggerDistance, + required double triggerDistance, this.visibleRange: default_VisibleRange, bool up: true, }) : assert(up != null), @@ -91,7 +91,7 @@ class RefreshWrapper extends Wrapper { class RefreshWrapperState extends State with TickerProviderStateMixin implements GestureProcessor { - AnimationController _sizeController; + late AnimationController _sizeController; /* up indicate drag from top (pull down) @@ -160,7 +160,7 @@ class RefreshWrapperState extends State void _handleOffsetCallBack() { if (widget.onOffsetChange != null) { - widget.onOffsetChange( + widget.onOffsetChange!( widget.up, _sizeController.value * widget.visibleRange); } } @@ -239,12 +239,12 @@ class LoadWrapper extends Wrapper { final bool autoLoad; LoadWrapper( - {Key key, - @required bool up, - @required ValueNotifier modeListener, - double triggerDistance, - this.autoLoad, - IndicatorBuilder builder}) + {Key? key, + required bool up, + required ValueNotifier modeListener, + required double triggerDistance, + required this.autoLoad, + required IndicatorBuilder builder}) : assert(up != null, modeListener != null), super( key: key, @@ -262,7 +262,7 @@ class LoadWrapper extends Wrapper { } class LoadWrapperState extends State implements GestureProcessor { - Function _updateListener; + late Function() _updateListener; @override Widget build(BuildContext context) { diff --git a/lib/pullrefresh/src/internals/refresh_physics.dart b/lib/pullrefresh/src/internals/refresh_physics.dart index 09a2e8e..13a33d7 100644 --- a/lib/pullrefresh/src/internals/refresh_physics.dart +++ b/lib/pullrefresh/src/internals/refresh_physics.dart @@ -18,11 +18,11 @@ class RefreshScrollPhysics extends ScrollPhysics { /// Creates scroll physics that bounce back from the edge. const RefreshScrollPhysics( - {ScrollPhysics parent, this.enableOverScroll: true}) + {ScrollPhysics? parent, this.enableOverScroll: true}) : super(parent: parent); @override - RefreshScrollPhysics applyTo(ScrollPhysics ancestor) { + RefreshScrollPhysics applyTo(ScrollPhysics? ancestor) { return new RefreshScrollPhysics( parent: buildParent(ancestor), enableOverScroll: enableOverScroll); } @@ -103,7 +103,7 @@ class RefreshScrollPhysics extends ScrollPhysics { } @override - Simulation createBallisticSimulation( + Simulation? createBallisticSimulation( ScrollMetrics position, double velocity) { final Tolerance tolerance = this.tolerance; if (velocity.abs() >= tolerance.velocity || position.outOfRange) { diff --git a/lib/pullrefresh/src/smart_refresher.dart b/lib/pullrefresh/src/smart_refresher.dart index c0f3747..817b344 100644 --- a/lib/pullrefresh/src/smart_refresher.dart +++ b/lib/pullrefresh/src/smart_refresher.dart @@ -45,18 +45,18 @@ class SmartRefresher extends StatefulWidget { // if open OverScroll if you use RefreshIndicator and LoadFooter final bool enableOverScroll; // upper and downer callback when you drag out of the distance - final OnRefresh onRefresh; + final OnRefresh? onRefresh; // This method will callback when the indicator changes from edge to edge. - final OnOffsetChange onOffsetChange; + final OnOffsetChange? onOffsetChange; //controll inner state final RefreshController controller; SmartRefresher({ - Key key, - @required this.child, - IndicatorBuilder headerBuilder, - IndicatorBuilder footerBuilder, - RefreshController controller, + Key? key, + required this.child, + IndicatorBuilder? headerBuilder, + IndicatorBuilder? footerBuilder, + RefreshController? controller, this.headerConfig: const RefreshConfig(), this.footerConfig: const LoadConfig(), this.enableOverScroll: default_enableOverScroll, @@ -65,7 +65,7 @@ class SmartRefresher extends StatefulWidget { this.onRefresh, this.onOffsetChange, }) : assert(child != null), - controller = controller ?? new RefreshController(), + controller = controller ?? RefreshController(), this.headerBuilder = headerBuilder ?? ((BuildContext context, int mode) { return new ClassicIndicator(mode: mode); @@ -177,7 +177,7 @@ class _SmartRefresherState extends State { } void _init() { - SchedulerBinding.instance.addPostFrameCallback((_) { + SchedulerBinding.instance?.addPostFrameCallback((_) { _onAfterBuild(); }); _scrollController.addListener(_handleOffsetCallback); @@ -209,21 +209,21 @@ class _SmartRefresherState extends State { if (overscrollPastStart > overscrollPastEnd) { if (widget.headerConfig is RefreshConfig) { if (widget.onOffsetChange != null) { - widget.onOffsetChange(true, overscrollPastStart); + widget.onOffsetChange!(true, overscrollPastStart); } } else { if (widget.onOffsetChange != null) { - widget.onOffsetChange(true, overscrollPastStart); + widget.onOffsetChange!(true, overscrollPastStart); } } } else if (overscrollPastEnd > 0) { if (widget.footerConfig is RefreshConfig) { if (widget.onOffsetChange != null) { - widget.onOffsetChange(false, overscrollPastEnd); + widget.onOffsetChange!(false, overscrollPastEnd); } } else { if (widget.onOffsetChange != null) { - widget.onOffsetChange(false, overscrollPastEnd); + widget.onOffsetChange!(false, overscrollPastEnd); } } } @@ -233,7 +233,7 @@ class _SmartRefresherState extends State { switch (mode.value) { case RefreshStatus.refreshing: if (widget.onRefresh != null) { - widget.onRefresh(up); + widget.onRefresh!(up); } if (up && widget.headerConfig is RefreshConfig) { RefreshConfig config = widget.headerConfig as RefreshConfig; @@ -259,10 +259,11 @@ class _SmartRefresherState extends State { _didChangeMode(false, bottomModeLis); }); setState(() { - if (widget.enablePullDown) - _headerHeight = _headerKey.currentContext.size.height; - if (widget.enablePullUp) { - _footerHeight = _footerKey.currentContext.size.height; + final size = _headerKey.currentContext?.size; + if (widget.enablePullDown && size is Size) + _headerHeight = size.height; + if (widget.enablePullUp && size is Size) { + _footerHeight = size.height; } }); } @@ -297,7 +298,7 @@ class _SmartRefresherState extends State { up: up, onOffsetChange: (bool up, double offset) { if (widget.onOffsetChange != null) { - widget.onOffsetChange( + widget.onOffsetChange!( up, up ? -_scrollController.offset + offset @@ -369,25 +370,23 @@ class _SmartRefresherState extends State { abstract class Indicator extends StatefulWidget { final int mode; - const Indicator({Key key, this.mode}) : super(key: key); + const Indicator({Key? key, required this.mode}) : super(key: key); } class RefreshController { - ValueNotifier _headerMode; - ValueNotifier _footerMode; + ValueNotifier? _headerMode; + ValueNotifier? _footerMode; ScrollController scrollController; - RefreshController() { - scrollController = ScrollController(); - } + RefreshController() : scrollController = ScrollController(); void requestRefresh(bool up) { - if (up) { - if (_headerMode.value == RefreshStatus.idle) - _headerMode.value = RefreshStatus.refreshing; - } else { - if (_footerMode.value == RefreshStatus.idle) { - _footerMode.value = RefreshStatus.refreshing; + if (up && _headerMode is ValueNotifier) { + if (_headerMode!.value == RefreshStatus.idle) + _headerMode!.value = RefreshStatus.refreshing; + } else if (_footerMode is ValueNotifier) { + if (_footerMode!.value == RefreshStatus.idle) { + _footerMode!.value = RefreshStatus.refreshing; } } } @@ -396,31 +395,31 @@ class RefreshController { scrollController.jumpTo(offset); } - Future animateTo( + Future animateTo( double offset, { - @required Duration duration, - @required Curve curve, + required Duration duration, + required Curve curve, }) { return scrollController.animateTo(offset, duration: duration, curve: curve); } void sendBack(bool up, int mode) { - if (up) { - _headerMode.value = mode; - } else { - _footerMode.value = mode; + if (up && _headerMode is ValueNotifier) { + _headerMode!.value = mode; + } else if (_footerMode is ValueNotifier) { + _footerMode!.value = mode; } } - int get headerMode => _headerMode.value; + int? get headerMode => _headerMode?.value; - int get footerMode => _footerMode.value; + int? get footerMode => _footerMode?.value; isRefresh(bool up) { - if (up) { - return _headerMode.value == RefreshStatus.refreshing; - } else { - return _footerMode.value == RefreshStatus.refreshing; + if (up && _headerMode is ValueNotifier) { + return _headerMode!.value == RefreshStatus.refreshing; + } else if (_footerMode is ValueNotifier) { + return _footerMode!.value == RefreshStatus.refreshing; } } } diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..cce5bc5 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,146 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + url: "https://pub.dartlang.org" + source: hosted + version: "2.7.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + characters: + dependency: transitive + description: + name: characters + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + charcode: + dependency: transitive + description: + name: charcode + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.1" + clock: + dependency: transitive + description: + name: clock + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + collection: + dependency: transitive + description: + name: collection + url: "https://pub.dartlang.org" + source: hosted + version: "1.15.0" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + matcher: + dependency: transitive + description: + name: matcher + url: "https://pub.dartlang.org" + source: hosted + version: "0.12.10" + meta: + dependency: transitive + description: + name: meta + url: "https://pub.dartlang.org" + source: hosted + version: "1.7.0" + path: + dependency: transitive + description: + name: path + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + url: "https://pub.dartlang.org" + source: hosted + version: "1.8.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + url: "https://pub.dartlang.org" + source: hosted + version: "1.10.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" + test_api: + dependency: transitive + description: + name: test_api + url: "https://pub.dartlang.org" + source: hosted + version: "0.3.0" + typed_data: + dependency: transitive + description: + name: typed_data + url: "https://pub.dartlang.org" + source: hosted + version: "1.3.0" + vector_math: + dependency: transitive + description: + name: vector_math + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.0" +sdks: + dart: ">=2.13.0 <3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index c60b542..6e1f311 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,10 +1,10 @@ name: flutter_section_table_view description: A iOS like table view (list view) including section, row, section header and divider, support pull refresh -version: 1.0.3 +version: 1.0.4 author: Realank homepage: https://github.com/Realank/flutter_section_table_view environment: - sdk: ">=2.0.0-dev.28.0 <3.0.0" + sdk: ">=2.13.0 <3.0.0" dependencies: flutter: