From 551a3873e11415a64ae4102516fe47bd6139b6d8 Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Sat, 18 May 2019 20:49:05 +0100 Subject: [PATCH 01/30] Updated error message formats --- .../widgets/sticky_header_layout_builder.dart | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/src/widgets/sticky_header_layout_builder.dart b/lib/src/widgets/sticky_header_layout_builder.dart index 984a328..fe2069b 100644 --- a/lib/src/widgets/sticky_header_layout_builder.dart +++ b/lib/src/widgets/sticky_header_layout_builder.dart @@ -96,16 +96,16 @@ class _StickyHeaderLayoutBuilderElement extends RenderObjectElement { built = widget.builder(this, constraints); debugWidgetBuilderValue(widget, built); } catch (e, stack) { - built = ErrorWidget - .builder(_debugReportException('building $widget', e, stack)); + built = ErrorWidget.builder( + _debugReportException('building $widget', e, stack)); } } try { _child = updateChild(_child, built, null); assert(_child != null); } catch (e, stack) { - built = ErrorWidget - .builder(_debugReportException('building $widget', e, stack)); + built = ErrorWidget.builder( + _debugReportException('building $widget', e, stack)); _child = updateChild(null, built, slot); } }); @@ -141,10 +141,11 @@ FlutterErrorDetails _debugReportException( StackTrace stack, ) { final FlutterErrorDetails details = new FlutterErrorDetails( - exception: exception, - stack: stack, - library: 'flutter_sticky_header widgets library', - context: context); + exception: exception, + stack: stack, + library: 'flutter_sticky_header widgets library', + context: ErrorDescription('context'), + ); FlutterError.reportError(details); return details; } From 25a71e38994f5fa513401b09d149677f5580be71 Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Sat, 18 May 2019 20:49:10 +0100 Subject: [PATCH 02/30] Updated SDK constraint to support new error message formats --- CHANGELOG.md | 5 +++++ pubspec.yaml | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad5ef16..b22f983 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.4.0 + +* Updated SDK constraint to support new error message formats. +* Updated error message formats + ## 0.3.4 ### Removed * Print call for headerPosition diff --git a/pubspec.yaml b/pubspec.yaml index 77e2f69..33d13bb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_sticky_header description: Flutter implementation of sticky headers as a sliver. Use it in a CustomScrollView. -version: 0.3.4 +version: 0.4.0 author: Romain Rastel homepage: https://github.com/letsar/flutter_sticky_header @@ -13,4 +13,5 @@ dev_dependencies: sdk: flutter environment: - sdk: ">=1.19.0 <3.0.0" + sdk: ">=2.2.0 <3.0.0" + flutter: ">=1.5.9-pre.94 <2.0.0" From 1a0d8f609c72a0182f1a44f59f0b24eb23057a3e Mon Sep 17 00:00:00 2001 From: Sebastian Roth Date: Sun, 19 May 2019 14:21:05 +0100 Subject: [PATCH 03/30] Match latest SliverHitTest APIs --- lib/src/rendering/sliver_sticky_header.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index e7d871c..6d90808 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -265,12 +265,13 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { } @override - bool hitTestChildren(HitTestResult result, + bool hitTestChildren(SliverHitTestResult result, {@required double mainAxisPosition, @required double crossAxisPosition}) { assert(geometry.hitTestExtent > 0.0); if (header != null && mainAxisPosition - constraints.overlap <= _headerExtent) { - return hitTestBoxChild(result, header, + return hitTestBoxChild( + BoxHitTestResult.wrap(SliverHitTestResult.wrap(result)), header, mainAxisPosition: mainAxisPosition - constraints.overlap, crossAxisPosition: crossAxisPosition) || (_overlapsContent && From 66735426094fcd32c7b43ef767e45f4cc6d0ff67 Mon Sep 17 00:00:00 2001 From: Romain Rastel Date: Wed, 10 Jul 2019 19:13:57 +0200 Subject: [PATCH 04/30] Update README.md Update last version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3be2638..9521d2f 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ In the `pubspec.yaml` of your flutter project, add the following dependency: ```yaml dependencies: ... - flutter_sticky_header: "^0.3.4" + flutter_sticky_header: "^0.4.0" ``` In your library add the following import: From 2d7f2b1cf03e04fb645973e1b8bea0dbaa9fb098 Mon Sep 17 00:00:00 2001 From: Romain Date: Fri, 14 Feb 2020 12:03:38 +0100 Subject: [PATCH 05/30] Added not sticky feature Rewrote all samples --- CHANGELOG.md | 4 + README.md | 39 +- example/.gitignore | 34 +- example/.metadata | 6 +- example/README.md | 12 +- example/android/.gitignore | 13 +- example/android/app/build.gradle | 32 +- .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 23 +- .../fsh/example/example/MainActivity.java | 13 - .../com/letsar/fsh/example/MainActivity.kt | 12 + .../app/src/profile/AndroidManifest.xml | 7 + example/android/build.gradle | 4 +- example/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/android/gradlew | 0 example/android/gradlew.bat | 180 ++++----- example/ios/.gitignore | 65 ++-- example/ios/Flutter/AppFrameworkInfo.plist | 2 +- example/ios/Runner.xcodeproj/project.pbxproj | 130 +++++-- .../xcshareddata/xcschemes/Runner.xcscheme | 6 +- example/ios/Runner/AppDelegate.h | 6 - example/ios/Runner/AppDelegate.m | 13 - example/ios/Runner/AppDelegate.swift | 13 + .../Icon-App-1024x1024@1x.png | Bin 11112 -> 10932 bytes example/ios/Runner/Info.plist | 6 +- example/ios/Runner/Runner-Bridging-Header.h | 1 + example/ios/Runner/main.m | 9 - example/lib/common.dart | 54 +++ example/lib/examples/animated_header.dart | 72 ++++ example/lib/examples/grid.dart | 62 ++++ example/lib/examples/list.dart | 50 +++ example/lib/examples/not_sticky.dart | 51 +++ example/lib/examples/reverse.dart | 51 +++ example/lib/examples/side_header.dart | 95 +++++ example/lib/main.dart | 341 ++++-------------- example/pubspec.yaml | 24 +- lib/src/rendering/sliver_sticky_header.dart | 142 +++----- .../sticky_header_layout_builder.dart | 6 +- lib/src/widgets/sliver_sticky_header.dart | 44 ++- .../widgets/sticky_header_layout_builder.dart | 28 +- pubspec.yaml | 2 +- test/flutter_sticky_header_test.dart | 232 +++++++++++- 43 files changed, 1230 insertions(+), 666 deletions(-) create mode 100644 example/android/app/src/debug/AndroidManifest.xml delete mode 100644 example/android/app/src/main/java/com/letsar/fsh/example/example/MainActivity.java create mode 100644 example/android/app/src/main/kotlin/com/letsar/fsh/example/MainActivity.kt create mode 100644 example/android/app/src/profile/AndroidManifest.xml mode change 100644 => 100755 example/android/gradlew delete mode 100644 example/ios/Runner/AppDelegate.h delete mode 100644 example/ios/Runner/AppDelegate.m create mode 100644 example/ios/Runner/AppDelegate.swift create mode 100644 example/ios/Runner/Runner-Bridging-Header.h delete mode 100644 example/ios/Runner/main.m create mode 100644 example/lib/common.dart create mode 100644 example/lib/examples/animated_header.dart create mode 100644 example/lib/examples/grid.dart create mode 100644 example/lib/examples/list.dart create mode 100644 example/lib/examples/not_sticky.dart create mode 100644 example/lib/examples/reverse.dart create mode 100644 example/lib/examples/side_header.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index b22f983..6a64db8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.1 +### Added +* A sticky parameter to specify whether the header is sticky or not. + ## 0.4.0 * Updated SDK constraint to support new error message formats. diff --git a/README.md b/README.md index 9521d2f..fbb9897 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ A Flutter implementation of sticky headers with a sliver as a child. * Notifies when the header scrolls outside the viewport. * Can scroll in any direction. * Supports overlapping (AppBars for example). +* Supports not sticky headers (with `sticky: false` parameter). ## Getting started @@ -22,7 +23,7 @@ In the `pubspec.yaml` of your flutter project, add the following dependency: ```yaml dependencies: ... - flutter_sticky_header: "^0.4.0" + flutter_sticky_header: "^0.4.1" ``` In your library add the following import: @@ -38,24 +39,24 @@ For help getting started with Flutter, view the online [documentation](https://f You can place one or multiple `SliverStickyHeader`s inside a `CustomScrollView`. ```dart -new SliverStickyHeader( - header: new Container( +SliverStickyHeader( + header: Container( height: 60.0, color: Colors.lightBlue, padding: EdgeInsets.symmetric(horizontal: 16.0), alignment: Alignment.centerLeft, - child: new Text( + child: Text( 'Header #0', style: const TextStyle(color: Colors.white), ), ), - sliver: new SliverList( - delegate: new SliverChildBuilderDelegate( - (context, i) => new ListTile( - leading: new CircleAvatar( - child: new Text('0'), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, i) => ListTile( + leading: CircleAvatar( + child: Text('0'), ), - title: new Text('List tile #$i'), + title: Text('List tile #$i'), ), childCount: 4, ), @@ -70,25 +71,25 @@ If you want to change the header layout during its scroll, you can use the `Sliv The example belows changes the opacity of the header as it scrolls off the viewport. ```dart -new SliverStickyHeaderBuilder( - builder: (context, state) => new Container( +SliverStickyHeaderBuilder( + builder: (context, state) => Container( height: 60.0, color: (state.isPinned ? Colors.pink : Colors.lightBlue) .withOpacity(1.0 - state.scrollPercentage), padding: EdgeInsets.symmetric(horizontal: 16.0), alignment: Alignment.centerLeft, - child: new Text( + child: Text( 'Header #1', style: const TextStyle(color: Colors.white), ), ), - sliver: new SliverList( - delegate: new SliverChildBuilderDelegate( - (context, i) => new ListTile( - leading: new CircleAvatar( - child: new Text('0'), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, i) => ListTile( + leading: CircleAvatar( + child: Text('0'), ), - title: new Text('List tile #$i'), + title: Text('List tile #$i'), ), childCount: 4, ), diff --git a/example/.gitignore b/example/.gitignore index dee655c..ae1f183 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -1,9 +1,37 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp .DS_Store -.dart_tool/ +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies .packages +.pub-cache/ .pub/ +/build/ -build/ +# Web related +lib/generated_plugin_registrant.dart -.flutter-plugins +# Exceptions to above rules. +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/example/.metadata b/example/.metadata index 94ee986..5d1241e 100644 --- a/example/.metadata +++ b/example/.metadata @@ -4,5 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: c7ea3ca377e909469c68f2ab878a5bc53d3cf66b - channel: dev + revision: 9f5ff2306bb3e30b2b98eee79cd231b1336f41f4 + channel: stable + +project_type: app diff --git a/example/README.md b/example/README.md index 64a12f6..a135626 100644 --- a/example/README.md +++ b/example/README.md @@ -4,5 +4,13 @@ A new Flutter project. ## Getting Started -For help getting started with Flutter, view our online -[documentation](https://flutter.io/). +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) + +For help getting started with Flutter, view our +[online documentation](https://flutter.dev/docs), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/example/android/.gitignore b/example/android/.gitignore index 65b7315..bc2100d 100644 --- a/example/android/.gitignore +++ b/example/android/.gitignore @@ -1,10 +1,7 @@ -*.iml -*.class -.gradle +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat /local.properties -/.idea/workspace.xml -/.idea/libraries -.DS_Store -/build -/captures GeneratedPluginRegistrant.java diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index a23f7c0..bee1849 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -11,11 +11,26 @@ if (flutterRoot == null) { throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") } +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 27 + compileSdkVersion 28 + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } lintOptions { disable 'InvalidPackage' @@ -23,12 +38,12 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.letsar.fsh.example.example" + applicationId "com.letsar.fsh.example" minSdkVersion 16 - targetSdkVersion 27 - versionCode 1 - versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + targetSdkVersion 28 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { @@ -45,7 +60,8 @@ flutter { } dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.1' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' + androidTestImplementation 'androidx.test:runner:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..91b46c0 --- /dev/null +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 37fde23..1a441e9 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,12 +1,5 @@ - - - - + package="com.letsar.fsh.example"> - + + diff --git a/example/android/app/src/main/java/com/letsar/fsh/example/example/MainActivity.java b/example/android/app/src/main/java/com/letsar/fsh/example/example/MainActivity.java deleted file mode 100644 index 8672db0..0000000 --- a/example/android/app/src/main/java/com/letsar/fsh/example/example/MainActivity.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.letsar.fsh.example.example; - -import android.os.Bundle; -import io.flutter.app.FlutterActivity; -import io.flutter.plugins.GeneratedPluginRegistrant; - -public class MainActivity extends FlutterActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - GeneratedPluginRegistrant.registerWith(this); - } -} diff --git a/example/android/app/src/main/kotlin/com/letsar/fsh/example/MainActivity.kt b/example/android/app/src/main/kotlin/com/letsar/fsh/example/MainActivity.kt new file mode 100644 index 0000000..09a0f4c --- /dev/null +++ b/example/android/app/src/main/kotlin/com/letsar/fsh/example/MainActivity.kt @@ -0,0 +1,12 @@ +package com.letsar.fsh.example + +import androidx.annotation.NonNull; +import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugins.GeneratedPluginRegistrant + +class MainActivity: FlutterActivity() { + override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { + GeneratedPluginRegistrant.registerWith(flutterEngine); + } +} diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..91b46c0 --- /dev/null +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/example/android/build.gradle b/example/android/build.gradle index 4476887..3100ad2 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,11 +1,13 @@ buildscript { + ext.kotlin_version = '1.3.50' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:3.5.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 8bd86f6..38c8d45 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1 +1,4 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true +android.useAndroidX=true +android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index aa901e1..296b146 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip diff --git a/example/android/gradlew b/example/android/gradlew old mode 100644 new mode 100755 diff --git a/example/android/gradlew.bat b/example/android/gradlew.bat index 8a0b282..aec9973 100644 --- a/example/android/gradlew.bat +++ b/example/android/gradlew.bat @@ -1,90 +1,90 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/example/ios/.gitignore b/example/ios/.gitignore index 79cc4da..e96ef60 100644 --- a/example/ios/.gitignore +++ b/example/ios/.gitignore @@ -1,45 +1,32 @@ -.idea/ -.vagrant/ -.sconsign.dblite -.svn/ - -.DS_Store -*.swp -profile - -DerivedData/ -build/ -GeneratedPluginRegistrant.h -GeneratedPluginRegistrant.m - -.generated/ - -*.pbxuser *.mode1v3 *.mode2v3 +*.moved-aside +*.pbxuser *.perspectivev3 - -!default.pbxuser +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. !default.mode1v3 !default.mode2v3 +!default.pbxuser !default.perspectivev3 - -xcuserdata - -*.moved-aside - -*.pyc -*sync/ -Icon? -.tags* - -/Flutter/app.flx -/Flutter/app.zip -/Flutter/flutter_assets/ -/Flutter/App.framework -/Flutter/Flutter.framework -/Flutter/Generated.xcconfig -/ServiceDefinitions.json - -Pods/ -.symlinks/ diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 9367d48..6b4c0f7 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable App CFBundleIdentifier diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 623d510..85e59b4 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,16 +8,12 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; }; 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, ); }; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 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 */; }; - 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -41,17 +37,15 @@ /* Begin PBXFileReference section */ 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 = ""; }; - 2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; }; 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 = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; 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 = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; @@ -74,7 +68,6 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 2D5378251FAA1A9400D5DBA9 /* flutter_assets */, 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEBA1CF902C7004384FC /* Flutter.framework */, @@ -91,7 +84,6 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, ); sourceTree = ""; }; @@ -106,8 +98,6 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( - 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, - 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, @@ -115,6 +105,8 @@ 97C146F11CF9000F007C117D /* Supporting Files */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, ); path = Runner; sourceTree = ""; @@ -122,7 +114,6 @@ 97C146F11CF9000F007C117D /* Supporting Files */ = { isa = PBXGroup; children = ( - 97C146F21CF9000F007C117D /* main.m */, ); name = "Supporting Files"; sourceTree = ""; @@ -156,17 +147,18 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0910; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = "The Chromium Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; }; }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -188,11 +180,8 @@ buildActionMask = 2147483647; files = ( 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -235,8 +224,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, - 97C146F31CF9000F007C117D /* main.m in Sources */, + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -263,6 +251,83 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.letsar.fsh.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; @@ -277,12 +342,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -331,12 +398,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -360,6 +429,8 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -370,7 +441,8 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CURRENT_PROJECT_VERSION = 1; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -382,8 +454,11 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.letsar.fsh.example.example; + PRODUCT_BUNDLE_IDENTIFIER = com.letsar.fsh.example; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -393,7 +468,8 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CURRENT_PROJECT_VERSION = 1; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -405,8 +481,10 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.letsar.fsh.example.example; + PRODUCT_BUNDLE_IDENTIFIER = com.letsar.fsh.example; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; @@ -419,6 +497,7 @@ buildConfigurations = ( 97C147031CF9000F007C117D /* Debug */, 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -428,6 +507,7 @@ buildConfigurations = ( 97C147061CF9000F007C117D /* Debug */, 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 1263ac8..a28140c 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ @@ -46,7 +45,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" @@ -67,7 +65,7 @@ -#import - -@interface AppDelegate : FlutterAppDelegate - -@end diff --git a/example/ios/Runner/AppDelegate.m b/example/ios/Runner/AppDelegate.m deleted file mode 100644 index 59a72e9..0000000 --- a/example/ios/Runner/AppDelegate.m +++ /dev/null @@ -1,13 +0,0 @@ -#include "AppDelegate.h" -#include "GeneratedPluginRegistrant.h" - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application - didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [GeneratedPluginRegistrant registerWithRegistry:self]; - // Override point for customization after application launch. - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} - -@end diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000..70693e4 --- /dev/null +++ b/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png index 3d43d11e66f4de3da27ed045ca4fe38ad8b48094..dc9ada4725e9b0ddb1deab583e5b5102493aa332 100644 GIT binary patch literal 10932 zcmeHN2~<R zh`|8`A_PQ1nSu(UMFx?8j8PC!!VDphaL#`F42fd#7Vlc`zIE4n%Y~eiz4y1j|NDpi z?<@|pSJ-HM`qifhf@m%MamgwK83`XpBA<+azdF#2QsT{X@z0A9Bq>~TVErigKH1~P zRX-!h-f0NJ4Mh++{D}J+K>~~rq}d%o%+4dogzXp7RxX4C>Km5XEI|PAFDmo;DFm6G zzjVoB`@qW98Yl0Kvc-9w09^PrsobmG*Eju^=3f?0o-t$U)TL1B3;sZ^!++3&bGZ!o-*6w?;oOhf z=A+Qb$scV5!RbG+&2S}BQ6YH!FKb0``VVX~T$dzzeSZ$&9=X$3)_7Z{SspSYJ!lGE z7yig_41zpQ)%5dr4ff0rh$@ky3-JLRk&DK)NEIHecf9c*?Z1bUB4%pZjQ7hD!A0r-@NF(^WKdr(LXj|=UE7?gBYGgGQV zidf2`ZT@pzXf7}!NH4q(0IMcxsUGDih(0{kRSez&z?CFA0RVXsVFw3^u=^KMtt95q z43q$b*6#uQDLoiCAF_{RFc{!H^moH_cmll#Fc^KXi{9GDl{>%+3qyfOE5;Zq|6#Hb zp^#1G+z^AXfRKaa9HK;%b3Ux~U@q?xg<2DXP%6k!3E)PA<#4$ui8eDy5|9hA5&{?v z(-;*1%(1~-NTQ`Is1_MGdQ{+i*ccd96ab$R$T3=% zw_KuNF@vI!A>>Y_2pl9L{9h1-C6H8<)J4gKI6{WzGBi<@u3P6hNsXG=bRq5c+z;Gc3VUCe;LIIFDmQAGy+=mRyF++u=drBWV8-^>0yE9N&*05XHZpPlE zxu@?8(ZNy7rm?|<+UNe0Vs6&o?l`Pt>P&WaL~M&#Eh%`rg@Mbb)J&@DA-wheQ>hRV z<(XhigZAT z>=M;URcdCaiO3d^?H<^EiEMDV+7HsTiOhoaMX%P65E<(5xMPJKxf!0u>U~uVqnPN7T!X!o@_gs3Ct1 zlZ_$5QXP4{Aj645wG_SNT&6m|O6~Tsl$q?nK*)(`{J4b=(yb^nOATtF1_aS978$x3 zx>Q@s4i3~IT*+l{@dx~Hst21fR*+5}S1@cf>&8*uLw-0^zK(+OpW?cS-YG1QBZ5q! zgTAgivzoF#`cSz&HL>Ti!!v#?36I1*l^mkrx7Y|K6L#n!-~5=d3;K<;Zqi|gpNUn_ z_^GaQDEQ*jfzh;`j&KXb66fWEk1K7vxQIMQ_#Wu_%3 z4Oeb7FJ`8I>Px;^S?)}2+4D_83gHEq>8qSQY0PVP?o)zAv3K~;R$fnwTmI-=ZLK`= zTm+0h*e+Yfr(IlH3i7gUclNH^!MU>id$Jw>O?2i0Cila#v|twub21@e{S2v}8Z13( zNDrTXZVgris|qYm<0NU(tAPouG!QF4ZNpZPkX~{tVf8xY690JqY1NVdiTtW+NqyRP zZ&;T0ikb8V{wxmFhlLTQ&?OP7 z;(z*<+?J2~z*6asSe7h`$8~Se(@t(#%?BGLVs$p``;CyvcT?7Y!{tIPva$LxCQ&4W z6v#F*);|RXvI%qnoOY&i4S*EL&h%hP3O zLsrFZhv&Hu5tF$Lx!8(hs&?!Kx5&L(fdu}UI5d*wn~A`nPUhG&Rv z2#ixiJdhSF-K2tpVL=)5UkXRuPAFrEW}7mW=uAmtVQ&pGE-&az6@#-(Te^n*lrH^m@X-ftVcwO_#7{WI)5v(?>uC9GG{lcGXYJ~Q8q zbMFl7;t+kV;|;KkBW2!P_o%Czhw&Q(nXlxK9ak&6r5t_KH8#1Mr-*0}2h8R9XNkr zto5-b7P_auqTJb(TJlmJ9xreA=6d=d)CVbYP-r4$hDn5|TIhB>SReMfh&OVLkMk-T zYf%$taLF0OqYF?V{+6Xkn>iX@TuqQ?&cN6UjC9YF&%q{Ut3zv{U2)~$>-3;Dp)*(? zg*$mu8^i=-e#acaj*T$pNowo{xiGEk$%DusaQiS!KjJH96XZ-hXv+jk%ard#fu=@Q z$AM)YWvE^{%tDfK%nD49=PI|wYu}lYVbB#a7wtN^Nml@CE@{Gv7+jo{_V?I*jkdLD zJE|jfdrmVbkfS>rN*+`#l%ZUi5_bMS<>=MBDNlpiSb_tAF|Zy`K7kcp@|d?yaTmB^ zo?(vg;B$vxS|SszusORgDg-*Uitzdi{dUV+glA~R8V(?`3GZIl^egW{a919!j#>f` znL1o_^-b`}xnU0+~KIFLQ)$Q6#ym%)(GYC`^XM*{g zv3AM5$+TtDRs%`2TyR^$(hqE7Y1b&`Jd6dS6B#hDVbJlUXcG3y*439D8MrK!2D~6gn>UD4Imctb z+IvAt0iaW73Iq$K?4}H`7wq6YkTMm`tcktXgK0lKPmh=>h+l}Y+pDtvHnG>uqBA)l zAH6BV4F}v$(o$8Gfo*PB>IuaY1*^*`OTx4|hM8jZ?B6HY;F6p4{`OcZZ(us-RVwDx zUzJrCQlp@mz1ZFiSZ*$yX3c_#h9J;yBE$2g%xjmGF4ca z&yL`nGVs!Zxsh^j6i%$a*I3ZD2SoNT`{D%mU=LKaEwbN(_J5%i-6Va?@*>=3(dQy` zOv%$_9lcy9+(t>qohkuU4r_P=R^6ME+wFu&LA9tw9RA?azGhjrVJKy&8=*qZT5Dr8g--d+S8zAyJ$1HlW3Olryt`yE zFIph~Z6oF&o64rw{>lgZISC6p^CBer9C5G6yq%?8tC+)7*d+ib^?fU!JRFxynRLEZ zj;?PwtS}Ao#9whV@KEmwQgM0TVP{hs>dg(1*DiMUOKHdQGIqa0`yZnHk9mtbPfoLx zo;^V6pKUJ!5#n`w2D&381#5#_t}AlTGEgDz$^;u;-vxDN?^#5!zN9ngytY@oTv!nc zp1Xn8uR$1Z;7vY`-<*?DfPHB;x|GUi_fI9@I9SVRv1)qETbNU_8{5U|(>Du84qP#7 z*l9Y$SgA&wGbj>R1YeT9vYjZuC@|{rajTL0f%N@>3$DFU=`lSPl=Iv;EjuGjBa$Gw zHD-;%YOE@<-!7-Mn`0WuO3oWuL6tB2cpPw~Nvuj|KM@))ixuDK`9;jGMe2d)7gHin zS<>k@!x;!TJEc#HdL#RF(`|4W+H88d4V%zlh(7#{q2d0OQX9*FW^`^_<3r$kabWAB z$9BONo5}*(%kx zOXi-yM_cmB3>inPpI~)duvZykJ@^^aWzQ=eQ&STUa}2uT@lV&WoRzkUoE`rR0)`=l zFT%f|LA9fCw>`enm$p7W^E@U7RNBtsh{_-7vVz3DtB*y#*~(L9+x9*wn8VjWw|Q~q zKFsj1Yl>;}%MG3=PY`$g$_mnyhuV&~O~u~)968$0b2!Jkd;2MtAP#ZDYw9hmK_+M$ zb3pxyYC&|CuAbtiG8HZjj?MZJBFbt`ryf+c1dXFuC z0*ZQhBzNBd*}s6K_G}(|Z_9NDV162#y%WSNe|FTDDhx)K!c(mMJh@h87@8(^YdK$&d*^WQe8Z53 z(|@MRJ$Lk-&ii74MPIs80WsOFZ(NX23oR-?As+*aq6b?~62@fSVmM-_*cb1RzZ)`5$agEiL`-E9s7{GM2?(KNPgK1(+c*|-FKoy}X(D_b#etO|YR z(BGZ)0Ntfv-7R4GHoXp?l5g#*={S1{u-QzxCGng*oWr~@X-5f~RA14b8~B+pLKvr4 zfgL|7I>jlak9>D4=(i(cqYf7#318!OSR=^`xxvI!bBlS??`xxWeg?+|>MxaIdH1U~#1tHu zB{QMR?EGRmQ_l4p6YXJ{o(hh-7Tdm>TAX380TZZZyVkqHNzjUn*_|cb?T? zt;d2s-?B#Mc>T-gvBmQZx(y_cfkXZO~{N zT6rP7SD6g~n9QJ)8F*8uHxTLCAZ{l1Y&?6v)BOJZ)=R-pY=Y=&1}jE7fQ>USS}xP#exo57uND0i*rEk@$;nLvRB@u~s^dwRf?G?_enN@$t* zbL%JO=rV(3Ju8#GqUpeE3l_Wu1lN9Y{D4uaUe`g>zlj$1ER$6S6@{m1!~V|bYkhZA z%CvrDRTkHuajMU8;&RZ&itnC~iYLW4DVkP<$}>#&(`UO>!n)Po;Mt(SY8Yb`AS9lt znbX^i?Oe9r_o=?})IHKHoQGKXsps_SE{hwrg?6dMI|^+$CeC&z@*LuF+P`7LfZ*yr+KN8B4{Nzv<`A(wyR@!|gw{zB6Ha ziwPAYh)oJ(nlqSknu(8g9N&1hu0$vFK$W#mp%>X~AU1ay+EKWcFdif{% z#4!4aoVVJ;ULmkQf!ke2}3hqxLK>eq|-d7Ly7-J9zMpT`?dxo6HdfJA|t)?qPEVBDv z{y_b?4^|YA4%WW0VZd8C(ZgQzRI5(I^)=Ub`Y#MHc@nv0w-DaJAqsbEHDWG8Ia6ju zo-iyr*sq((gEwCC&^TYBWt4_@|81?=B-?#P6NMff(*^re zYqvDuO`K@`mjm_Jd;mW_tP`3$cS?R$jR1ZN09$YO%_iBqh5ftzSpMQQtxKFU=FYmP zeY^jph+g<4>YO;U^O>-NFLn~-RqlHvnZl2yd2A{Yc1G@Ga$d+Q&(f^tnPf+Z7serIU};17+2DU_f4Z z@GaPFut27d?!YiD+QP@)T=77cR9~MK@bd~pY%X(h%L={{OIb8IQmf-!xmZkm8A0Ga zQSWONI17_ru5wpHg3jI@i9D+_Y|pCqVuHJNdHUauTD=R$JcD2K_liQisqG$(sm=k9;L* z!L?*4B~ql7uioSX$zWJ?;q-SWXRFhz2Jt4%fOHA=Bwf|RzhwqdXGr78y$J)LR7&3T zE1WWz*>GPWKZ0%|@%6=fyx)5rzUpI;bCj>3RKzNG_1w$fIFCZ&UR0(7S?g}`&Pg$M zf`SLsz8wK82Vyj7;RyKmY{a8G{2BHG%w!^T|Njr!h9TO2LaP^_f22Q1=l$QiU84ao zHe_#{S6;qrC6w~7{y(hs-?-j?lbOfgH^E=XcSgnwW*eEz{_Z<_iT7q6h&WAVr806i~>Gqn6rM z>3}bMG&oq%DIriqR35=rtEdos5L6z)YC*Xq0U-$_+Il@RaU zXYX%+``hR28`(B*uJ6G9&iz>|)PS%!)9N`7=LcmcxH}k69HPyT-%S zH7+jBCC<%76cg_H-n41cTqnKn`u_V9p~XaTLUe3s{KRPSTeK6apP4Jg%VQ$e#72ms zxyWzmGSRwN?=fRgpx!?W&ZsrLfuhAsRxm%;_|P@3@3~BJwY4ZVBJ3f&$5x>`^fD?d zI+z!v#$!gz%FtL*%mR^Uwa*8LJFZ_;X!y$cD??W#c)31l@ervOa_Qk86R{HJiZb$f z&&&0xYmB{@D@yl~^l5IXtB_ou{xFiYP(Jr<9Ce{jCN z<3Rf2TD%}_N?y>bgWq|{`RKd}n>P4e8Z-D+(fn^4)+|pv$DcR&i+RHNhv$71F*McT zl`phYBlb;wO`b7)*10XF6UXhY9`@UR*6-#(Zp`vyU(__*te6xYtV&N0(zjMtev{tZ zapmGin===teMXjsS0>CYxUy<2izOKOPai0}!B9+6q$s3CF8W{xUwz?A0ADO5&BsiB z{SFt|KehNd-S#eiDq!y&+mW9N_!wH-i~q|oNm=mEzkx}B?Ehe%q$tK8f=QY#*6rH9 zNHHaG(9WBqzP!!TMEktSVuh$i$4A^b25LK}&1*4W?ul*5pZYjL1OZ@X9?3W7Y|T6} z1SXx0Wn-|!A;fZGGlYn9a1Jz5^8)~v#mXhmm>um{QiGG459N}L<&qyD+sy_ixD@AP zW0XV6w#3(JW>TEV}MD=O0O>k5H>p#&|O zD2mGf0Cz7+>l7`NuzGobt;(o@vb9YiOpHN8QJ9Uva|i7R?7nnq;L_iq+ZqPv*oGu! zN@GuJ9fm;yrEFga63m?1qy|5&fd32<%$yP$llh}Udrp>~fb>M>R55I@BsGYhCj8m1 zC=ziFh4@hoytpfrJlr}FsV|C(aV4PZ^8^`G29(+!Bk8APa#PemJqkF zE{IzwPaE)I&r`OxGk*vPErm6sGKaQJ&6FODW$;gAl_4b_j!oH4yE@ zP~Cl4?kp>Ccc~Nm+0kjIb`U0N7}zrQEN5!Ju|}t}LeXi!baZOyhlWha5lq{Ld2rdo zGz7hAJQt<6^cxXTe0xZjmADL85cC&H+~Lt2siIIh{$~+U#&#^{Ub22IA|ea6 z5j12XLc`~dh$$1>3o0Cgvo*ybi$c*z>n=5L&X|>Wy1~eagk;lcEnf^2^2xB=e58Z` z@Rw{1ssK)NRV+2O6c<8qFl%efHE;uy!mq(Xi1P*H2}LMi z3EqWN2U?eW{J$lSFxDJg-=&RH!=6P9!y|S~gmjg)gPKGMxq6r9cNIhW` zS})-obO}Ao_`;=>@fAwU&=|5$J;?~!s4LN2&XiMXEl>zk9M}tVEg#kkIkbKp%Ig2QJ2aCILCM1E=aN*iuz>;q#T_I7aVM=E4$m_#OWLnXQnFUnu?~(X>$@NP zBJ@Zw>@bmErSuW7SR2=6535wh-R`WZ+5dLqwTvw}Ks8~4F#hh0$Qn^l-z=;>D~St( z-1yEjCCgd*z5qXa*bJ7H2Tk54KiX&=Vd}z?%dcc z`N8oeYUKe17&|B5A-++RHh8WQ%;gN{vf%05@jZF%wn1Z_yk#M~Cn(i@MB_mpcbLj5 zR#QAtC`k=tZ*h|){Mjz`7bNL zGWOW=bjQhX@`Vw^xn#cVwn28c2D9vOb0TLLy~-?-%gOyHSeJ9a>P}5OF5$n}k-pvUa*pvLw)KvG~>QjNWS3LY1f*OkFwPZ5qC@+3^Bt=HZbf`alKY#{pn zdY}NEIgo1sd)^TPxVzO{uvU$|Z-jkK0p1x##LexgQ$zx1^bNPOG*u2RmZkIM!zFVz zz|IsP3I?qrlmjGS2w_(azCvGTnf~flqogV@Q%mH{76uLU(>UB zQZ?*ys3BO&TV{Pj_qEa-hkH7mOMe_Bnu3%CXCgu90XNKf$N)PUc3Ei-&~@tT zI^49Lm^+=TrI=h4h=W@jW{GjWd{_kVuSzAL6Pi@HKYYnnNbtcYdIRww+jY$(30=#p8*if(mzbvau z00#}4Qf+gH&ce_&8y3Z@CZV>b%&Zr7xuPSSqOmoaP@arwPrMx^jQBQQi>YvBUdpBn zI``MZ3I3HLqp)@vk^E|~)zw$0$VI_RPsL9u(kqulmS`tnb%4U)hm{)h@bG*jw@Y*#MX;Th1wu3TrO}Srn_+YWYesEgkO1 zv?P8uWB)is;#&=xBBLf+y5e4?%y>_8$1KwkAJ8UcW|0CIz89{LydfJKr^RF=JFPi}MAv|ecbuZ!YcTSxsD$(Pr#W*oytl?@+2 zXBFb32Kf_G3~EgOS7C`8w!tx}DcCT%+#qa76VSbnHo;4(oJ7)}mm?b5V65ir`7Z}s zR2)m15b#E}z_2@rf34wo!M^CnVoi# ze+S(IK({C6u=Sm{1>F~?)8t&fZpOOPcby;I3jO;7^xmLKM(<%i-nyj9mgw9F1Lq4|DZUHZ4)V9&6fQM(ZxbG{h+}(koiTu`SQw6#6q2Yg z-d+1+MRp$zYT2neIR2cKij2!R;C~ooQ3<;^8)_Gch&ZyEtiQwmF0Mb_)6)4lVEBF< zklXS7hvtu30uJR`3OzcqUNOdYsfrKSGkIQAk|4=&#ggxdU4^Y(;)$8}fQ>lTgQdJ{ zzie8+1$3@E;|a`kzuFh9Se}%RHTmBg)h$eH;gttjL_)pO^10?!bNev6{mLMaQpY<< z7M^ZXrg>tw;vU@9H=khbff?@nu)Yw4G% zGxobPTUR2p_ed7Lvx?dkrN^>Cv$Axuwk;Wj{5Z@#$sK@f4{7SHg%2bpcS{(~s;L(mz@9r$cK@m~ef&vf%1@ z@8&@LLO2lQso|bJD6}+_L1*D^}>oqg~$NipL>QlP3 zM#ATSy@ycMkKs5-0X8nFAtMhO_=$DlWR+@EaZ}`YduRD4A2@!at3NYRHmlENea9IF zN*s>mi?zy*Vv+F+&4-o`Wj}P3mLGM*&M(z|;?d82>hQkkY?e-hJ47mWOLCPL*MO04 z3lE(n2RM=IIo;Z?I=sKJ_h=iJHbQ2<}WW0b@I6Qf-{T=Qn#@N0yG5xH&ofEy^mZMPzd22nR`t!Q)VkNgf*VOxE z$XhOunG3ZN#`Ks$Hp~}`OX5vmHP={GYUJ+-g0%PS$*Qi5+-40M47zJ24vK1#? zb$s^%r?+>#lw$mpZaMa1aO%wlPm3~cno_(S%U&-R;6eK(@`CjswAW2)HfZ>ptItaZ|XqQ z&sHVVL>WCe|E4iPb2~gS5ITs6xfg(kmt&3$YcI=zTuqj37t|+9ojCr(G^ul#p{>k) zM94pI>~5VZ$!*Qurq<@RIXgP3sx-2kL$1Q~da%rnNIh?)&+c~*&e~CYPDhPYjb+Xu zKg5w^XB3(_9{Waa4E(-J-Kq_u6t_k?a8kEHqai-N-4#`SRerO!h}!cS%SMC<)tGix zOzVP^_t!HN&HIPL-ZpcgWitHM&yFRC7!k4zSI+-<_uQ}|tX)n{Ib;X>Xx>i_d*KkH zCzogKQFpP1408_2!ofU|iBq2R8hW6G zuqJs9Tyw{u%-uWczPLkM!MfKfflt+NK9Vk8E!C>AsJwNDRoe2~cL+UvqNP|5J8t)( z0$iMa!jhudJ+fqFn+um&@Oj6qXJd_3-l`S^I1#0fnt!z3?D*hAHr*u(*wR@`4O z#avrtg%s`Fh{?$FtBFM^$@@hW!8ZfF4;=n0<8In&X}-Rp=cd0TqT_ne46$j^r}FzE z26vX^!PzScuQfFfl1HEZ{zL?G88mcc76zHGizWiykBf4m83Z${So-+dZ~YGhm*RO7 zB1gdIdqnFi?qw+lPRFW5?}CQ3Me3G^muvll&4iN+*5#_mmIu;loULMwb4lu9U*dFM z-Sr**(0Ei~u=$3<6>C-G6z4_LNCx||6YtjS)<;hf)YJTPKXW+w%hhCTUAInIse9>r zl2YU6nRb$u-FJlWN*{{%sm_gi_UP5{=?5}5^D2vPzM=oPfNw~azZQ#P zl5z8RtSSiTIpEohC15i-Q1Bk{3&ElsD0uGAOxvbk29VUDmmA0w;^v`W#0`};O3DVE z&+-ca*`YcN%z*#VXWK9Qa-OEME#fykF%|7o=1Y+eF;Rtv0W4~kKRDx9YBHOWhC%^I z$Jec0cC7o37}Xt}cu)NH5R}NT+=2Nap*`^%O)vz?+{PV<2~qX%TzdJOGeKj5_QjqR&a3*K@= P-1+_A+?hGkL;m(J7kc&K diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 90181b7..a060db6 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -15,11 +15,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + $(FLUTTER_BUILD_NAME) CFBundleSignature ???? CFBundleVersion - 1 + $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000..7335fdf --- /dev/null +++ b/example/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" \ No newline at end of file diff --git a/example/ios/Runner/main.m b/example/ios/Runner/main.m deleted file mode 100644 index dff6597..0000000 --- a/example/ios/Runner/main.m +++ /dev/null @@ -1,9 +0,0 @@ -#import -#import -#import "AppDelegate.h" - -int main(int argc, char* argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/example/lib/common.dart b/example/lib/common.dart new file mode 100644 index 0000000..d965970 --- /dev/null +++ b/example/lib/common.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; + +class AppScaffold extends StatelessWidget { + const AppScaffold({ + Key key, + @required this.title, + @required this.slivers, + this.reverse = false, + }) : super(key: key); + + final String title; + final List slivers; + final bool reverse; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(title), + ), + body: CustomScrollView( + slivers: slivers, + reverse: reverse, + ), + ); + } +} + +class Header extends StatelessWidget { + const Header({ + Key key, + this.index, + this.title, + this.color = Colors.lightBlue, + }) : super(key: key); + + final String title; + final int index; + final Color color; + + @override + Widget build(BuildContext context) { + return Container( + height: 60, + color: color, + padding: EdgeInsets.symmetric(horizontal: 16.0), + alignment: Alignment.centerLeft, + child: Text( + title ?? 'Header #$index', + style: const TextStyle(color: Colors.white), + ), + ); + } +} diff --git a/example/lib/examples/animated_header.dart b/example/lib/examples/animated_header.dart new file mode 100644 index 0000000..882b07a --- /dev/null +++ b/example/lib/examples/animated_header.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_sticky_header/flutter_sticky_header.dart'; + +import '../common.dart'; + +class AnimatedHeaderExample extends StatelessWidget { + const AnimatedHeaderExample({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return AppScaffold( + title: 'Animated header Example', + slivers: [ + _StickyHeaderList(index: 0), + _StickyHeaderList(index: 1), + _StickyHeaderList(index: 2), + _StickyHeaderList(index: 3), + ], + ); + } +} + +class _StickyHeaderList extends StatelessWidget { + const _StickyHeaderList({ + Key key, + this.index, + }) : super(key: key); + + final int index; + + @override + Widget build(BuildContext context) { + return SliverStickyHeaderBuilder( + builder: (context, state) => _AnimatedHeader( + state: state, + index: index, + ), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, i) => ListTile( + leading: CircleAvatar( + child: Text('$index'), + ), + title: Text('List tile #$i'), + ), + childCount: 6, + ), + ), + ); + } +} + +class _AnimatedHeader extends StatelessWidget { + const _AnimatedHeader({ + Key key, + this.state, + this.index, + }) : super(key: key); + + final int index; + final SliverStickyHeaderState state; + + @override + Widget build(BuildContext context) { + return Header( + index: index, + color: Colors.lightBlue.withOpacity(1 - state.scrollPercentage), + ); + } +} diff --git a/example/lib/examples/grid.dart b/example/lib/examples/grid.dart new file mode 100644 index 0000000..f68e3d5 --- /dev/null +++ b/example/lib/examples/grid.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_sticky_header/flutter_sticky_header.dart'; + +import '../common.dart'; + +class GridExample extends StatelessWidget { + const GridExample({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return AppScaffold( + title: 'Grid Example', + slivers: [ + _StickyHeaderGrid(index: 0), + _StickyHeaderGrid(index: 1), + _StickyHeaderGrid(index: 2), + _StickyHeaderGrid(index: 3), + ], + ); + } +} + +class _StickyHeaderGrid extends StatelessWidget { + const _StickyHeaderGrid({ + Key key, + this.index, + }) : super(key: key); + + final int index; + + @override + Widget build(BuildContext context) { + return SliverStickyHeader( + header: Header(index: index), + sliver: SliverGrid( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, crossAxisSpacing: 4.0, mainAxisSpacing: 4.0), + delegate: SliverChildBuilderDelegate( + (context, i) => GridTile( + child: Card( + child: Container( + color: Colors.green, + ), + ), + footer: Container( + color: Colors.white.withOpacity(0.5), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'Grid tile #$i', + style: const TextStyle(color: Colors.black), + ), + ), + ), + ), + childCount: 9, + ), + ), + ); + } +} diff --git a/example/lib/examples/list.dart b/example/lib/examples/list.dart new file mode 100644 index 0000000..f94f841 --- /dev/null +++ b/example/lib/examples/list.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_sticky_header/flutter_sticky_header.dart'; + +import '../common.dart'; + +class ListExample extends StatelessWidget { + const ListExample({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return AppScaffold( + title: 'List Example', + slivers: [ + _StickyHeaderList(index: 0), + _StickyHeaderList(index: 1), + _StickyHeaderList(index: 2), + _StickyHeaderList(index: 3), + ], + ); + } +} + +class _StickyHeaderList extends StatelessWidget { + const _StickyHeaderList({ + Key key, + this.index, + }) : super(key: key); + + final int index; + + @override + Widget build(BuildContext context) { + return SliverStickyHeader( + header: Header(index: index), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, i) => ListTile( + leading: CircleAvatar( + child: Text('$index'), + ), + title: Text('List tile #$i'), + ), + childCount: 6, + ), + ), + ); + } +} diff --git a/example/lib/examples/not_sticky.dart b/example/lib/examples/not_sticky.dart new file mode 100644 index 0000000..af205f2 --- /dev/null +++ b/example/lib/examples/not_sticky.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_sticky_header/flutter_sticky_header.dart'; + +import '../common.dart'; + +class NotStickyExample extends StatelessWidget { + const NotStickyExample({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return AppScaffold( + title: 'Not Sticky Example', + slivers: [ + _NotStickyList(index: 0), + _NotStickyList(index: 1), + _NotStickyList(index: 2), + _NotStickyList(index: 3), + ], + ); + } +} + +class _NotStickyList extends StatelessWidget { + const _NotStickyList({ + Key key, + this.index, + }) : super(key: key); + + final int index; + + @override + Widget build(BuildContext context) { + return SliverStickyHeader( + header: Header(index: index), + sticky: false, + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, i) => ListTile( + leading: CircleAvatar( + child: Text('$index'), + ), + title: Text('List tile #$i'), + ), + childCount: 6, + ), + ), + ); + } +} diff --git a/example/lib/examples/reverse.dart b/example/lib/examples/reverse.dart new file mode 100644 index 0000000..9279f76 --- /dev/null +++ b/example/lib/examples/reverse.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_sticky_header/flutter_sticky_header.dart'; + +import '../common.dart'; + +class ReverseExample extends StatelessWidget { + const ReverseExample({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return AppScaffold( + reverse: true, + title: 'Reverse Example', + slivers: [ + _StickyHeaderList(index: 0), + _StickyHeaderList(index: 1), + _StickyHeaderList(index: 2), + _StickyHeaderList(index: 3), + ], + ); + } +} + +class _StickyHeaderList extends StatelessWidget { + const _StickyHeaderList({ + Key key, + this.index, + }) : super(key: key); + + final int index; + + @override + Widget build(BuildContext context) { + return SliverStickyHeader( + header: Header(index: index), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, i) => ListTile( + leading: CircleAvatar( + child: Text('$index'), + ), + title: Text('List tile #$i'), + ), + childCount: 6, + ), + ), + ); + } +} diff --git a/example/lib/examples/side_header.dart b/example/lib/examples/side_header.dart new file mode 100644 index 0000000..3fd77e9 --- /dev/null +++ b/example/lib/examples/side_header.dart @@ -0,0 +1,95 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_sticky_header/flutter_sticky_header.dart'; + +import '../common.dart'; + +class SideHeaderExample extends StatelessWidget { + const SideHeaderExample({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return AppScaffold( + title: 'Side Header Example', + slivers: [ + _StickyHeaderGrid(index: 0), + _StickyHeaderGrid(index: 1), + _StickyHeaderGrid(index: 2), + _StickyHeaderGrid(index: 3), + ], + ); + } +} + +class _StickyHeaderGrid extends StatelessWidget { + const _StickyHeaderGrid({ + Key key, + this.index, + }) : super(key: key); + + final int index; + + @override + Widget build(BuildContext context) { + return SliverStickyHeader( + overlapsContent: true, + header: _SideHeader(index: index), + sliver: SliverPadding( + padding: const EdgeInsets.only(left: 60), + sliver: SliverGrid( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, crossAxisSpacing: 4.0, mainAxisSpacing: 4.0), + delegate: SliverChildBuilderDelegate( + (context, i) => GridTile( + child: Card( + child: Container( + color: Colors.green, + ), + ), + footer: Container( + color: Colors.white.withOpacity(0.5), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'Grid tile #$i', + style: const TextStyle(color: Colors.black), + ), + ), + ), + ), + childCount: 9, + ), + ), + ), + ); + } +} + +class _SideHeader extends StatelessWidget { + const _SideHeader({ + Key key, + this.index, + }) : super(key: key); + + final int index; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), + child: Align( + alignment: Alignment.centerLeft, + child: SizedBox( + height: 44.0, + width: 44.0, + child: CircleAvatar( + backgroundColor: Colors.orangeAccent, + foregroundColor: Colors.white, + child: Text('$index'), + ), + ), + ), + ); + ; + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index dc06166..71b9818 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,310 +1,95 @@ import 'package:flutter/material.dart'; -import 'package:flutter_sticky_header/flutter_sticky_header.dart'; -void main() => runApp(new MyApp()); +import 'examples/list.dart'; +import 'examples/grid.dart'; +import 'examples/not_sticky.dart'; +import 'examples/side_header.dart'; +import 'examples/animated_header.dart'; +import 'examples/reverse.dart'; + +void main() => runApp(const App()); + +class App extends StatelessWidget { + const App({ + Key key, + }) : super(key: key); -class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return new MaterialApp( - title: 'Sticky Header example', - theme: new ThemeData( - primarySwatch: Colors.blue, - ), - home: new MainScreen(), + return MaterialApp( + title: 'Flutter Sticky Headers', + home: const _Home(), ); } } -class MainScreen extends StatelessWidget { +class _Home extends StatelessWidget { + const _Home({ + Key key, + }) : super(key: key); + @override Widget build(BuildContext context) { - return new SimpleScaffold( - title: 'Flutter Sticky Header example', - child: new Builder(builder: (BuildContext context) { - return new CustomScrollView( - slivers: _buildSlivers(context), - ); - }), - ); - } - - List _buildSlivers(BuildContext context) { - List slivers = new List(); - - //slivers.add(_buildExample()); - //slivers.add(_buildBuilderExample()); - int i = 0; - slivers.add(SliverAppBar( - backgroundColor: Colors.blue.withOpacity(0.5), - title: Text('text'), - pinned: true, - )); - slivers.add(SliverAppBar( - backgroundColor: Colors.yellow.withOpacity(0.5), - title: Text('text'), - pinned: true, - )); - slivers.addAll(_buildHeaderBuilderLists(context, i, i += 5)); - slivers.addAll(_buildLists(context, i, i += 3)); - slivers.addAll(_buildGrids(context, i, i += 3)); - slivers.addAll(_buildSideHeaderGrids(context, i, i += 3)); - slivers.addAll(_buildHeaderBuilderLists(context, i, i += 5)); - return slivers; - } - - List _buildLists(BuildContext context, int firstIndex, int count) { - return List.generate(count, (sliverIndex) { - sliverIndex += firstIndex; - return new SliverStickyHeader( - header: _buildHeader(sliverIndex), - sliver: new SliverList( - delegate: new SliverChildBuilderDelegate( - (context, i) => new ListTile( - leading: new CircleAvatar( - child: new Text('$sliverIndex'), - ), - title: new Text('List tile #$i'), - ), - childCount: 4, + return Scaffold( + appBar: AppBar( + title: Text('Flutter Sticky Headers'), + ), + body: ListView( + children: [ + _Item( + text: 'List Example', + builder: (_) => const ListExample(), ), - ), - ); - }); - } - - List _buildHeaderBuilderLists( - BuildContext context, int firstIndex, int count) { - return List.generate(count, (sliverIndex) { - sliverIndex += firstIndex; - return new SliverStickyHeaderBuilder( - builder: (context, state) => - _buildAnimatedHeader(context, sliverIndex, state), - sliver: new SliverList( - delegate: new SliverChildBuilderDelegate( - (context, i) => new ListTile( - leading: new CircleAvatar( - child: new Text('$sliverIndex'), - ), - title: new Text('List tile #$i'), - ), - childCount: 4, + _Item( + text: 'Grid Example', + builder: (_) => const GridExample(), ), - ), - ); - }); - } - - List _buildGrids(BuildContext context, int firstIndex, int count) { - return List.generate(count, (sliverIndex) { - sliverIndex += firstIndex; - return new SliverStickyHeader( - header: _buildHeader(sliverIndex), - sliver: new SliverGrid( - gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, crossAxisSpacing: 4.0, mainAxisSpacing: 4.0), - delegate: new SliverChildBuilderDelegate( - (context, i) => GestureDetector( - onTap: () => Scaffold.of(context).showSnackBar( - new SnackBar(content: Text('Grid tile #$i'))), - child: new GridTile( - child: Card( - child: new Container( - color: Colors.green, - ), - ), - footer: new Container( - color: Colors.white.withOpacity(0.5), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: new Text( - 'Grid tile #$i', - style: const TextStyle(color: Colors.black), - ), - ), - ), - ), - ), - childCount: 9, + _Item( + text: 'Not Sticky Example', + builder: (_) => const NotStickyExample(), ), - ), - ); - }); - } - - List _buildSideHeaderGrids( - BuildContext context, int firstIndex, int count) { - return List.generate(count, (sliverIndex) { - sliverIndex += firstIndex; - return new SliverStickyHeader( - overlapsContent: true, - header: _buildSideHeader(context, sliverIndex), - sliver: new SliverPadding( - padding: new EdgeInsets.only(left: 60.0), - sliver: new SliverGrid( - gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - crossAxisSpacing: 4.0, - mainAxisSpacing: 4.0, - childAspectRatio: 1.0), - delegate: new SliverChildBuilderDelegate( - (context, i) => GestureDetector( - onTap: () => Scaffold.of(context).showSnackBar( - new SnackBar(content: Text('Grid tile #$i'))), - child: new GridTile( - child: Card( - child: new Container( - color: Colors.orange, - ), - ), - footer: new Container( - color: Colors.white.withOpacity(0.5), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: new Text( - 'Grid tile #$i', - style: const TextStyle(color: Colors.black), - ), - ), - ), - ), - ), - childCount: 12, - ), + _Item( + text: 'Side Header Example', + builder: (_) => const SideHeaderExample(), ), - ), - ); - }); - } - - Widget _buildHeader(int index, {String text}) { - return new Container( - height: 60.0, - color: Colors.lightBlue, - padding: EdgeInsets.symmetric(horizontal: 16.0), - alignment: Alignment.centerLeft, - child: new Text( - text ?? 'Header #$index', - style: const TextStyle(color: Colors.white), - ), - ); - } - - Widget _buildSideHeader(BuildContext context, int index, {String text}) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), - child: Align( - alignment: Alignment.centerLeft, - child: new SizedBox( - height: 44.0, - width: 44.0, - child: GestureDetector( - onTap: () => Scaffold - .of(context) - .showSnackBar(new SnackBar(content: Text('$index'))), - child: new CircleAvatar( - backgroundColor: Colors.orangeAccent, - foregroundColor: Colors.white, - child: new Text('$index'), - ), + _Item( + text: 'Animated Header Example', + builder: (_) => const AnimatedHeaderExample(), ), - ), - ), - ); - } - - Widget _buildAnimatedHeader( - BuildContext context, int index, SliverStickyHeaderState state) { - return GestureDetector( - onTap: () => Scaffold - .of(context) - .showSnackBar(new SnackBar(content: Text('$index'))), - child: new Container( - height: 60.0, - color: (state.isPinned ? Colors.pink : Colors.lightBlue) - .withOpacity(1.0 - state.scrollPercentage), - padding: EdgeInsets.symmetric(horizontal: 16.0), - alignment: Alignment.centerLeft, - child: new Text( - 'Header #$index', - style: const TextStyle(color: Colors.white), - ), - ), - ); - } - - Widget _buildExample() { - return new SliverStickyHeader( - header: new Container( - height: 60.0, - color: Colors.lightBlue, - padding: EdgeInsets.symmetric(horizontal: 16.0), - alignment: Alignment.centerLeft, - child: new Text( - 'Header #0', - style: const TextStyle(color: Colors.white), - ), - ), - sliver: new SliverList( - delegate: new SliverChildBuilderDelegate( - (context, i) => new ListTile( - leading: new CircleAvatar( - child: new Text('0'), - ), - title: new Text('List tile #$i'), - ), - childCount: 4, - ), - ), - ); - } - - Widget _buildBuilderExample() { - return new SliverStickyHeaderBuilder( - builder: (context, state) => new Container( - height: 60.0, - color: (state.isPinned ? Colors.pink : Colors.lightBlue) - .withOpacity(1.0 - state.scrollPercentage), - padding: EdgeInsets.symmetric(horizontal: 16.0), - alignment: Alignment.centerLeft, - child: new Text( - 'Header #1', - style: const TextStyle(color: Colors.white), - ), + _Item( + text: 'Reverse List Example', + builder: (_) => const ReverseExample(), ), - sliver: new SliverList( - delegate: new SliverChildBuilderDelegate( - (context, i) => new ListTile( - leading: new CircleAvatar( - child: new Text('0'), - ), - title: new Text('List tile #$i'), - ), - childCount: 4, - ), + ], ), ); } } -class SimpleScaffold extends StatelessWidget { - const SimpleScaffold({ +class _Item extends StatelessWidget { + const _Item({ Key key, - this.title, - this.child, + @required this.text, + @required this.builder, }) : super(key: key); - final String title; - - final Widget child; + final String text; + final WidgetBuilder builder; @override Widget build(BuildContext context) { - return new Scaffold( - appBar: new AppBar( - title: new Text(title), + return Card( + color: Colors.blue, + child: InkWell( + onTap: () => Navigator.push(context, MaterialPageRoute(builder: builder)), + child: Container( + padding: EdgeInsets.all(16), + child: Text( + text, + style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 20), + ), + ), ), - body: child, ); } } diff --git a/example/pubspec.yaml b/example/pubspec.yaml index b2da0ad..ade1327 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,6 +1,21 @@ name: example description: A new Flutter project. +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: ">=2.7.0 <3.0.0" + dependencies: flutter: sdk: flutter @@ -12,13 +27,14 @@ dependencies: flutter_sticky_header: path: ../ + dev_dependencies: flutter_test: sdk: flutter # For information on the generic Dart part of this file, see the -# following page: https://www.dartlang.org/tools/pub/pubspec +# following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter. flutter: @@ -34,10 +50,10 @@ flutter: # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.io/assets-and-images/#resolution-aware. + # https://flutter.dev/assets-and-images/#resolution-aware. # For details regarding adding assets from package dependencies, see - # https://flutter.io/assets-and-images/#from-packages + # https://flutter.dev/assets-and-images/#from-packages # To add custom fonts to your application, add a fonts section here, # in this "flutter" section. Each entry in this list should have a @@ -57,4 +73,4 @@ flutter: # weight: 700 # # For details regarding fonts from package dependencies, - # see https://flutter.io/custom-fonts/#from-packages + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index 6d90808..74f5389 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -14,9 +14,12 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { RenderSliverStickyHeader({ RenderObject header, RenderSliver child, - overlapsContent: false, + bool overlapsContent: false, + bool sticky: true, }) : assert(overlapsContent != null), - _overlapsContent = overlapsContent { + assert(sticky != null), + _overlapsContent = overlapsContent, + _sticky = sticky { this.header = header; this.child = child; } @@ -34,6 +37,15 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { markNeedsLayout(); } + bool get sticky => _sticky; + bool _sticky; + set sticky(bool value) { + assert(value != null); + if (_sticky == value) return; + _sticky = value; + markNeedsLayout(); + } + /// The render object's header RenderBox get header => _header; RenderBox _header; @@ -54,8 +66,7 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { @override void setupParentData(RenderObject child) { - if (child.parentData is! SliverPhysicalParentData) - child.parentData = new SliverPhysicalParentData(); + if (child.parentData is! SliverPhysicalParentData) child.parentData = SliverPhysicalParentData(); } @override @@ -119,13 +130,12 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { } // One of them is not null. - AxisDirection axisDirection = applyGrowthDirectionToAxisDirection( - constraints.axisDirection, constraints.growthDirection); + AxisDirection axisDirection = applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection); if (header != null) { header.layout( - new StickyHeaderConstraints( - state: _oldState ?? new SliverStickyHeaderState(0.0, false), + StickyHeaderConstraints( + state: _oldState ?? SliverStickyHeaderState(0.0, false), boxConstraints: constraints.asBoxConstraints(), ), parentUsesSize: true, @@ -135,59 +145,48 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { // Compute the header extent only one time. double headerExtent = headerLogicalExtent; - final double headerPaintExtent = - calculatePaintOffset(constraints, from: 0.0, to: headerExtent); - final double headerCacheExtent = - calculateCacheOffset(constraints, from: 0.0, to: headerExtent); + final double headerPaintExtent = calculatePaintOffset(constraints, from: 0.0, to: headerExtent); + final double headerCacheExtent = calculateCacheOffset(constraints, from: 0.0, to: headerExtent); if (child == null) { - geometry = new SliverGeometry( + geometry = SliverGeometry( scrollExtent: headerExtent, maxPaintExtent: headerExtent, paintExtent: headerPaintExtent, cacheExtent: headerCacheExtent, hitTestExtent: headerPaintExtent, - hasVisualOverflow: headerExtent > constraints.remainingPaintExtent || - constraints.scrollOffset > 0.0); + hasVisualOverflow: headerExtent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0); } else { child.layout( constraints.copyWith( scrollOffset: math.max(0.0, constraints.scrollOffset - headerExtent), cacheOrigin: math.min(0.0, constraints.cacheOrigin + headerExtent), overlap: 0.0, - remainingPaintExtent: - constraints.remainingPaintExtent - headerPaintExtent, - remainingCacheExtent: - constraints.remainingCacheExtent - headerCacheExtent, + remainingPaintExtent: constraints.remainingPaintExtent - headerPaintExtent, + remainingCacheExtent: constraints.remainingCacheExtent - headerCacheExtent, ), parentUsesSize: true, ); final SliverGeometry childLayoutGeometry = child.geometry; if (childLayoutGeometry.scrollOffsetCorrection != null) { - geometry = new SliverGeometry( + geometry = SliverGeometry( scrollOffsetCorrection: childLayoutGeometry.scrollOffsetCorrection, ); return; } final double paintExtent = math.min( - headerPaintExtent + - math.max(childLayoutGeometry.paintExtent, - childLayoutGeometry.layoutExtent), - constraints.remainingPaintExtent); + headerPaintExtent + math.max(childLayoutGeometry.paintExtent, childLayoutGeometry.layoutExtent), + constraints.remainingPaintExtent, + ); - geometry = new SliverGeometry( + geometry = SliverGeometry( scrollExtent: headerExtent + childLayoutGeometry.scrollExtent, paintExtent: paintExtent, - layoutExtent: math.min( - headerPaintExtent + childLayoutGeometry.layoutExtent, paintExtent), - cacheExtent: math.min( - headerCacheExtent + childLayoutGeometry.cacheExtent, - constraints.remainingCacheExtent), + layoutExtent: math.min(headerPaintExtent + childLayoutGeometry.layoutExtent, paintExtent), + cacheExtent: math.min(headerCacheExtent + childLayoutGeometry.cacheExtent, constraints.remainingCacheExtent), maxPaintExtent: headerExtent + childLayoutGeometry.maxPaintExtent, - hitTestExtent: math.max( - headerPaintExtent + childLayoutGeometry.paintExtent, - headerPaintExtent + childLayoutGeometry.hitTestExtent), + hitTestExtent: math.max(headerPaintExtent + childLayoutGeometry.paintExtent, headerPaintExtent + childLayoutGeometry.hitTestExtent), hasVisualOverflow: childLayoutGeometry.hasVisualOverflow, ); @@ -199,13 +198,10 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { childParentData.paintOffset = Offset.zero; break; case AxisDirection.right: - childParentData.paintOffset = new Offset( - calculatePaintOffset(constraints, from: 0.0, to: headerExtent), - 0.0); + childParentData.paintOffset = Offset(calculatePaintOffset(constraints, from: 0.0, to: headerExtent), 0.0); break; case AxisDirection.down: - childParentData.paintOffset = new Offset(0.0, - calculatePaintOffset(constraints, from: 0.0, to: headerExtent)); + childParentData.paintOffset = Offset(0.0, calculatePaintOffset(constraints, from: 0.0, to: headerExtent)); break; case AxisDirection.left: childParentData.paintOffset = Offset.zero; @@ -215,28 +211,22 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { if (header != null) { final SliverPhysicalParentData headerParentData = header.parentData; - final childScrollExtent = child?.geometry?.scrollExtent ?? 0.0; - double headerPosition = math.min( - constraints.overlap, - childScrollExtent - - constraints.scrollOffset - - (overlapsContent ? _headerExtent : 0.0)); - - _isPinned = (constraints.scrollOffset + constraints.overlap) > 0.0 || - constraints.remainingPaintExtent == - constraints.viewportMainAxisExtent; + final double childScrollExtent = child?.geometry?.scrollExtent ?? 0.0; + final double headerPosition = sticky + ? math.min(constraints.overlap, childScrollExtent - constraints.scrollOffset - (overlapsContent ? _headerExtent : 0.0)) + : -constraints.scrollOffset; + + _isPinned = sticky && + ((constraints.scrollOffset + constraints.overlap) > 0.0 || constraints.remainingPaintExtent == constraints.viewportMainAxisExtent); // second layout if scroll percentage changed and header is a RenderStickyHeaderLayoutBuilder. if (header is RenderStickyHeaderLayoutBuilder) { - double scrollPercentage = - ((headerPosition - constraints.overlap).abs() / _headerExtent) - .clamp(0.0, 1.0); + double scrollPercentage = ((headerPosition - constraints.overlap).abs() / _headerExtent).clamp(0.0, 1.0); - SliverStickyHeaderState state = - new SliverStickyHeaderState(scrollPercentage, _isPinned); + SliverStickyHeaderState state = SliverStickyHeaderState(scrollPercentage, _isPinned); if (_oldState != state) { _oldState = state; header.layout( - new StickyHeaderConstraints( + StickyHeaderConstraints( state: _oldState, boxConstraints: constraints.asBoxConstraints(), ), @@ -247,57 +237,45 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { switch (axisDirection) { case AxisDirection.up: - headerParentData.paintOffset = new Offset( - 0.0, geometry.paintExtent - headerPosition - _headerExtent); + headerParentData.paintOffset = Offset(0.0, geometry.paintExtent - headerPosition - _headerExtent); break; case AxisDirection.down: - headerParentData.paintOffset = new Offset(0.0, headerPosition); + headerParentData.paintOffset = Offset(0.0, headerPosition); break; case AxisDirection.left: - headerParentData.paintOffset = new Offset( - geometry.paintExtent - headerPosition - _headerExtent, 0.0); + headerParentData.paintOffset = Offset(geometry.paintExtent - headerPosition - _headerExtent, 0.0); break; case AxisDirection.right: - headerParentData.paintOffset = new Offset(headerPosition, 0.0); + headerParentData.paintOffset = Offset(headerPosition, 0.0); break; } } } @override - bool hitTestChildren(SliverHitTestResult result, - {@required double mainAxisPosition, @required double crossAxisPosition}) { + bool hitTestChildren(SliverHitTestResult result, {@required double mainAxisPosition, @required double crossAxisPosition}) { assert(geometry.hitTestExtent > 0.0); - if (header != null && - mainAxisPosition - constraints.overlap <= _headerExtent) { + if (header != null && mainAxisPosition - constraints.overlap <= _headerExtent) { return hitTestBoxChild( - BoxHitTestResult.wrap(SliverHitTestResult.wrap(result)), header, - mainAxisPosition: mainAxisPosition - constraints.overlap, - crossAxisPosition: crossAxisPosition) || + BoxHitTestResult.wrap(SliverHitTestResult.wrap(result)), + header, + mainAxisPosition: mainAxisPosition - constraints.overlap, + crossAxisPosition: crossAxisPosition, + ) || (_overlapsContent && child != null && child.geometry.hitTestExtent > 0.0 && - child.hitTest(result, - mainAxisPosition: - mainAxisPosition - childMainAxisPosition(child), - crossAxisPosition: crossAxisPosition)); + child.hitTest(result, mainAxisPosition: mainAxisPosition - childMainAxisPosition(child), crossAxisPosition: crossAxisPosition)); } else if (child != null && child.geometry.hitTestExtent > 0.0) { - return child.hitTest(result, - mainAxisPosition: mainAxisPosition - childMainAxisPosition(child), - crossAxisPosition: crossAxisPosition); + return child.hitTest(result, mainAxisPosition: mainAxisPosition - childMainAxisPosition(child), crossAxisPosition: crossAxisPosition); } return false; } @override double childMainAxisPosition(RenderObject child) { - if (child == header) - return _isPinned - ? 0.0 - : -(constraints.scrollOffset + constraints.overlap); - if (child == this.child) - return calculatePaintOffset(constraints, - from: 0.0, to: headerLogicalExtent); + if (child == header) return _isPinned ? 0.0 : -(constraints.scrollOffset + constraints.overlap); + if (child == this.child) return calculatePaintOffset(constraints, from: 0.0, to: headerLogicalExtent); return null; } @@ -326,7 +304,7 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { context.paintChild(child, offset + childParentData.paintOffset); } - // The header must be draw over the sliver. + // The header must be drawn over the sliver. if (header != null) { final SliverPhysicalParentData headerParentData = header.parentData; context.paintChild(header, offset + headerParentData.paintOffset); diff --git a/lib/src/rendering/sticky_header_layout_builder.dart b/lib/src/rendering/sticky_header_layout_builder.dart index d9a5d7c..a082ce1 100644 --- a/lib/src/rendering/sticky_header_layout_builder.dart +++ b/lib/src/rendering/sticky_header_layout_builder.dart @@ -2,8 +2,7 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_sticky_header/src/rendering/sticky_header_constraints.dart'; -class RenderStickyHeaderLayoutBuilder extends RenderBox - with RenderObjectWithChildMixin { +class RenderStickyHeaderLayoutBuilder extends RenderBox with RenderObjectWithChildMixin { RenderStickyHeaderLayoutBuilder({ LayoutCallback callback, }) : _callback = callback; @@ -23,8 +22,7 @@ class RenderStickyHeaderLayoutBuilder extends RenderBox bool _debugThrowIfNotCheckingIntrinsics() { assert(() { if (!RenderObject.debugCheckingIntrinsics) { - throw new FlutterError( - 'StickyHeaderLayoutBuilder does not support returning intrinsic dimensions.\n' + throw FlutterError('StickyHeaderLayoutBuilder does not support returning intrinsic dimensions.\n' 'Calculating the intrinsic dimensions would require running the layout ' 'callback speculatively, which might mutate the live render object tree.'); } diff --git a/lib/src/widgets/sliver_sticky_header.dart b/lib/src/widgets/sliver_sticky_header.dart index 6937505..ec588f7 100644 --- a/lib/src/widgets/sliver_sticky_header.dart +++ b/lib/src/widgets/sliver_sticky_header.dart @@ -5,7 +5,9 @@ import 'package:flutter_sticky_header/src/widgets/sticky_header_layout_builder.d /// Signature used by [SliverStickyHeaderBuilder] to build the header /// when the sticky header state has changed. typedef Widget SliverStickyHeaderWidgetBuilder( - BuildContext context, SliverStickyHeaderState state); + BuildContext context, + SliverStickyHeaderState state, +); /// State describing how a sticky header is rendered. @immutable @@ -25,8 +27,7 @@ class SliverStickyHeaderState { if (identical(this, other)) return true; if (other is! SliverStickyHeaderState) return false; final SliverStickyHeaderState typedOther = other; - return scrollPercentage == typedOther.scrollPercentage && - isPinned == typedOther.isPinned; + return scrollPercentage == typedOther.scrollPercentage && isPinned == typedOther.isPinned; } @override @@ -44,13 +45,15 @@ class SliverStickyHeader extends RenderObjectWidget { /// The [header] stays pinned when it hits the start of the viewport until /// the [sliver] scrolls off the viewport. /// - /// The [overlapsContent] argument must not be null. + /// The [overlapsContent] and [sticky] arguments must not be null. SliverStickyHeader({ Key key, this.header, this.sliver, this.overlapsContent: false, + this.sticky = true, }) : assert(overlapsContent != null), + assert(sticky != null), super(key: key); /// The header to display before the sliver. @@ -63,21 +66,26 @@ class SliverStickyHeader extends RenderObjectWidget { /// instead of before. final bool overlapsContent; + /// Whether to stick the header. + /// Defaults to true. + final bool sticky; + @override RenderSliverStickyHeader createRenderObject(BuildContext context) { - return new RenderSliverStickyHeader( + return RenderSliverStickyHeader( overlapsContent: overlapsContent, + sticky: sticky, ); } @override - SliverStickyHeaderRenderObjectElement createElement() => - new SliverStickyHeaderRenderObjectElement(this); + SliverStickyHeaderRenderObjectElement createElement() => SliverStickyHeaderRenderObjectElement(this); @override - void updateRenderObject( - BuildContext context, RenderSliverStickyHeader renderObject) { - renderObject..overlapsContent = overlapsContent; + void updateRenderObject(BuildContext context, RenderSliverStickyHeader renderObject) { + renderObject + ..overlapsContent = overlapsContent + ..sticky = sticky; } } @@ -91,14 +99,16 @@ class SliverStickyHeaderBuilder extends StatelessWidget { /// Creates a widget that builds the header of a [SliverStickyHeader] /// each time its scroll percentage changes. /// - /// The [builder] and [overlapsContent] arguments must not be null. + /// The [builder], [overlapsContent] and [sticky] arguments must not be null. const SliverStickyHeaderBuilder({ Key key, @required this.builder, this.sliver, this.overlapsContent: false, + this.sticky = true, }) : assert(builder != null), assert(overlapsContent != null), + assert(sticky != null), super(key: key); /// Called to build the [SliverStickyHeader]'s header. @@ -114,12 +124,17 @@ class SliverStickyHeaderBuilder extends StatelessWidget { /// instead of before. final bool overlapsContent; + /// Whether to stick the header. + /// Defaults to true. + final bool sticky; + @override Widget build(BuildContext context) { - return new SliverStickyHeader( + return SliverStickyHeader( overlapsContent: overlapsContent, sliver: sliver, - header: new StickyHeaderLayoutBuilder( + sticky: sticky, + header: StickyHeaderLayoutBuilder( builder: (context, constraints) => builder(context, constraints.state), ), ); @@ -128,8 +143,7 @@ class SliverStickyHeaderBuilder extends StatelessWidget { class SliverStickyHeaderRenderObjectElement extends RenderObjectElement { /// Creates an element that uses the given widget as its configuration. - SliverStickyHeaderRenderObjectElement(SliverStickyHeader widget) - : super(widget); + SliverStickyHeaderRenderObjectElement(SliverStickyHeader widget) : super(widget); @override SliverStickyHeader get widget => super.widget; diff --git a/lib/src/widgets/sticky_header_layout_builder.dart b/lib/src/widgets/sticky_header_layout_builder.dart index fe2069b..9d95cda 100644 --- a/lib/src/widgets/sticky_header_layout_builder.dart +++ b/lib/src/widgets/sticky_header_layout_builder.dart @@ -3,8 +3,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_sticky_header/src/rendering/sticky_header_constraints.dart'; import 'package:flutter_sticky_header/src/rendering/sticky_header_layout_builder.dart'; -typedef Widget StickyHeaderLayoutWidgetBuilder( - BuildContext context, StickyHeaderConstraints constraints); +typedef Widget StickyHeaderLayoutWidgetBuilder(BuildContext context, StickyHeaderConstraints constraints); /// Builds a widget tree that can depend on the [StickyHeaderConstraints]. /// @@ -25,19 +24,16 @@ class StickyHeaderLayoutBuilder extends RenderObjectWidget { final StickyHeaderLayoutWidgetBuilder builder; @override - RenderObjectElement createElement() => - new _StickyHeaderLayoutBuilderElement(this); + RenderObjectElement createElement() => _StickyHeaderLayoutBuilderElement(this); @override - RenderObject createRenderObject(BuildContext context) => - new RenderStickyHeaderLayoutBuilder(); + RenderObject createRenderObject(BuildContext context) => RenderStickyHeaderLayoutBuilder(); // updateRenderObject is redundant with the logic in the _StickyHeaderLayoutBuilderElement below. } class _StickyHeaderLayoutBuilderElement extends RenderObjectElement { - _StickyHeaderLayoutBuilderElement(StickyHeaderLayoutBuilder widget) - : super(widget); + _StickyHeaderLayoutBuilderElement(StickyHeaderLayoutBuilder widget) : super(widget); @override StickyHeaderLayoutBuilder get widget => super.widget; @@ -78,8 +74,7 @@ class _StickyHeaderLayoutBuilderElement extends RenderObjectElement { // This gets called if markNeedsBuild() is called on us. // That might happen if, e.g., our builder uses Inherited widgets. renderObject.markNeedsLayout(); - super - .performRebuild(); // Calls widget.updateRenderObject (a no-op in this case). + super.performRebuild(); // Calls widget.updateRenderObject (a no-op in this case). } @override @@ -96,16 +91,14 @@ class _StickyHeaderLayoutBuilderElement extends RenderObjectElement { built = widget.builder(this, constraints); debugWidgetBuilderValue(widget, built); } catch (e, stack) { - built = ErrorWidget.builder( - _debugReportException('building $widget', e, stack)); + built = ErrorWidget.builder(_debugReportException('building $widget', e, stack)); } } try { _child = updateChild(_child, built, null); assert(_child != null); } catch (e, stack) { - built = ErrorWidget.builder( - _debugReportException('building $widget', e, stack)); + built = ErrorWidget.builder(_debugReportException('building $widget', e, stack)); _child = updateChild(null, built, slot); } }); @@ -113,8 +106,7 @@ class _StickyHeaderLayoutBuilderElement extends RenderObjectElement { @override void insertChildRenderObject(RenderObject child, slot) { - final RenderObjectWithChildMixin renderObject = - this.renderObject; + final RenderObjectWithChildMixin renderObject = this.renderObject; assert(slot == null); assert(renderObject.debugValidateChild(child)); renderObject.child = child; @@ -140,11 +132,11 @@ FlutterErrorDetails _debugReportException( dynamic exception, StackTrace stack, ) { - final FlutterErrorDetails details = new FlutterErrorDetails( + final FlutterErrorDetails details = FlutterErrorDetails( exception: exception, stack: stack, library: 'flutter_sticky_header widgets library', - context: ErrorDescription('context'), + context: ErrorDescription(context), ); FlutterError.reportError(details); return details; diff --git a/pubspec.yaml b/pubspec.yaml index 33d13bb..00a0a03 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_sticky_header description: Flutter implementation of sticky headers as a sliver. Use it in a CustomScrollView. -version: 0.4.0 +version: 0.4.1 author: Romain Rastel homepage: https://github.com/letsar/flutter_sticky_header diff --git a/test/flutter_sticky_header_test.dart b/test/flutter_sticky_header_test.dart index ab73b3a..7d2339e 100644 --- a/test/flutter_sticky_header_test.dart +++ b/test/flutter_sticky_header_test.dart @@ -1 +1,231 @@ -void main() {} +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:flutter_sticky_header/flutter_sticky_header.dart'; + +void main() { + setUp(() { + WidgetsBinding.instance.renderView.configuration = TestViewConfiguration(size: Size(400, 800)); + }); + + testWidgets('Mix sticky and not sticky headers', (WidgetTester tester) async { + await tester.pumpWidget( + RepaintBoundary( + child: MaterialApp( + home: Scaffold( + body: CustomScrollView( + cacheExtent: 0, + slivers: [ + SliverStickyHeader( + header: _Header(index: 0), + sliver: const _Sliver(), + ), + SliverStickyHeader( + header: _Header(index: 1), + sticky: false, + sliver: const _Sliver(), + ), + SliverStickyHeader( + header: _Header(index: 2), + sliver: const _Sliver(), + ), + ], + ), + ), + ), + ), + ); + + final header00Finder = find.text('Header #0'); + final header01Finder = find.text('Header #1'); + final header02Finder = find.text('Header #2'); + + expect(header00Finder, findsOneWidget); + expect(header01Finder, findsNothing); + expect(header02Finder, findsNothing); + + final gesture = await tester.startGesture(Offset(200, 100)); + + // We scroll just before the Header #1. + await gesture.moveBy(Offset(0, -80)); + await tester.pump(); + + expect(header00Finder, findsOneWidget); + expect(header01Finder, findsNothing); + expect(header02Finder, findsNothing); + + // We scroll just after the Header #1 so that it is visible. + await gesture.moveBy(Offset(0, -80)); + await tester.pump(); + + expect(header00Finder, findsOneWidget); + expect(header01Finder, findsOneWidget); + expect(header02Finder, findsNothing); + + // We scroll in a way that Headers 0 and 1 are side by side. + await gesture.moveBy(Offset(0, -640)); + await tester.pump(); + + expect(header00Finder, findsOneWidget); + expect(header01Finder, findsOneWidget); + expect(header02Finder, findsNothing); + + // We scroll in a way that Header #1 is at the top of the screen. + await gesture.moveBy(Offset(0, -80)); + await tester.pump(); + + expect(header00Finder, findsNothing); + expect(header01Finder, findsOneWidget); + expect(header02Finder, findsNothing); + + // We scroll in a way that Header #1 is not visible. + await gesture.moveBy(Offset(0, -80)); + await tester.pump(); + + expect(header00Finder, findsNothing); + // Header #1 is in the tree (because the sliver is onstage). + expect(tester.getRect(header01Finder), const Rect.fromLTRB(0, -80, 400, 0)); + expect(header02Finder, findsNothing); + }); + + testWidgets('Mix sticky and not sticky headers - reverse', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: CustomScrollView( + cacheExtent: 0, + reverse: true, + slivers: [ + SliverStickyHeader( + header: _Header(index: 0), + sliver: const _Sliver(), + ), + SliverStickyHeader( + header: _Header(index: 1), + sticky: false, + sliver: const _Sliver(), + ), + SliverStickyHeader( + header: _Header(index: 2), + sliver: const _Sliver(), + ), + ], + ), + ), + ), + ); + + final header00Finder = find.text('Header #0'); + final header01Finder = find.text('Header #1'); + final header02Finder = find.text('Header #2'); + + expect(header00Finder, findsOneWidget); + expect(header01Finder, findsNothing); + expect(header02Finder, findsNothing); + + final gesture = await tester.startGesture(Offset(200, 100)); + + // We scroll just before the Header #1. + await gesture.moveBy(Offset(0, 80)); + await tester.pump(); + + expect(header00Finder, findsOneWidget); + expect(header01Finder, findsNothing); + expect(header02Finder, findsNothing); + + // We scroll just after the Header #1 so that it is visible. + await gesture.moveBy(Offset(0, 80)); + await tester.pump(); + + expect(header00Finder, findsOneWidget); + expect(header01Finder, findsOneWidget); + expect(header02Finder, findsNothing); + + // We scroll in a way that Headers 0 and 1 are side by side. + await gesture.moveBy(Offset(0, 640)); + await tester.pump(); + + expect(header00Finder, findsOneWidget); + expect(header01Finder, findsOneWidget); + expect(header02Finder, findsNothing); + + // We scroll in a way that Header #1 is at the top of the screen. + await gesture.moveBy(Offset(0, 80)); + await tester.pump(); + + expect(header00Finder, findsNothing); + expect(header01Finder, findsOneWidget); + expect(header02Finder, findsNothing); + + // We scroll in a way that Header #1 is no longer visible. + await gesture.moveBy(Offset(0, 80)); + await tester.pump(); + + expect(header00Finder, findsNothing); + // Header #1 is in the tree (because the sliver is onstage). + expect(tester.getRect(header01Finder), const Rect.fromLTRB(0, 800, 400, 880)); + expect(header02Finder, findsNothing); + }); +} + +class _Header extends StatelessWidget { + const _Header({ + Key key, + @required this.index, + }) : super(key: key); + + final int index; + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.blue, + child: Text('Header #$index'), + height: 80, + ); + } +} + +class _Sliver extends StatelessWidget { + const _Sliver({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return SliverList( + delegate: SliverChildBuilderDelegate( + (context, i) => const _SliverItem(), + childCount: 20, + ), + ); + } +} + +class _SliverItem extends StatelessWidget { + const _SliverItem({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox(height: 40); + } +} + +// class _IsOnScreen extends Matcher { +// const _IsOnScreen(); + +// @override +// bool matches(covariant Finder finder, Map matchState) { +// finder.ev +// return _hasAncestorMatching(finder, (Widget widget) { +// if (widget is Offstage) +// return widget.offstage; +// return false; +// }); +// } + +// @override +// Description describe(Description description) => description.add('onscreen'); +// } From f92f326652a6190d0ef2a456c2768dcabba6647c Mon Sep 17 00:00:00 2001 From: Romain Date: Fri, 14 Feb 2020 12:06:42 +0100 Subject: [PATCH 06/30] Removing author from pubspec --- pubspec.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 00a0a03..d2d33bd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,6 @@ name: flutter_sticky_header description: Flutter implementation of sticky headers as a sliver. Use it in a CustomScrollView. version: 0.4.1 -author: Romain Rastel homepage: https://github.com/letsar/flutter_sticky_header dependencies: From 44711e25d737ed6e83ccb001cc9d23031586d35b Mon Sep 17 00:00:00 2001 From: Romain Date: Fri, 21 Feb 2020 15:25:31 +0100 Subject: [PATCH 07/30] Add a controller which can be used to know the offset of the current sticky header --- CHANGELOG.md | 4 + README.md | 3 +- example/lib/common.dart | 40 ++- example/lib/examples/mix_slivers.dart | 62 +++++ example/lib/main.dart | 5 + lib/src/rendering/sliver_sticky_header.dart | 25 +- lib/src/widgets/sliver_sticky_header.dart | 120 ++++++++- pubspec.yaml | 2 +- test/controller_test.dart | 234 ++++++++++++++++++ ...icky_header_test.dart => sticky_test.dart} | 57 ++--- 10 files changed, 501 insertions(+), 51 deletions(-) create mode 100644 example/lib/examples/mix_slivers.dart create mode 100644 test/controller_test.dart rename test/{flutter_sticky_header_test.dart => sticky_test.dart} (83%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a64db8..bf445dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.2 +### Added +* A StickyHeaderController to get the scroll offset of the current sticky header. + ## 0.4.1 ### Added * A sticky parameter to specify whether the header is sticky or not. diff --git a/README.md b/README.md index fbb9897..b296f11 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ A Flutter implementation of sticky headers with a sliver as a child. * Can scroll in any direction. * Supports overlapping (AppBars for example). * Supports not sticky headers (with `sticky: false` parameter). +* Supports a controller which notifies the scroll offset of the current sticky header. ## Getting started @@ -23,7 +24,7 @@ In the `pubspec.yaml` of your flutter project, add the following dependency: ```yaml dependencies: ... - flutter_sticky_header: "^0.4.1" + flutter_sticky_header: "^0.4.2" ``` In your library add the following import: diff --git a/example/lib/common.dart b/example/lib/common.dart index d965970..a8b49a2 100644 --- a/example/lib/common.dart +++ b/example/lib/common.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_sticky_header/flutter_sticky_header.dart'; class AppScaffold extends StatelessWidget { const AppScaffold({ @@ -14,18 +15,43 @@ class AppScaffold extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text(title), - ), - body: CustomScrollView( - slivers: slivers, - reverse: reverse, + return DefaultStickyHeaderController( + child: Scaffold( + appBar: AppBar( + title: Text(title), + ), + body: CustomScrollView( + slivers: slivers, + reverse: reverse, + ), + floatingActionButton: const _FloatingActionButton(), ), ); } } +class _FloatingActionButton extends StatelessWidget { + const _FloatingActionButton({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return FloatingActionButton( + child: Icon(Icons.adjust), + backgroundColor: Colors.green, + onPressed: () { + final double offset = DefaultStickyHeaderController.of(context).stickyHeaderScrollOffset; + PrimaryScrollController.of(context).animateTo( + offset, + duration: Duration(milliseconds: 300), + curve: Curves.easeIn, + ); + }, + ); + } +} + class Header extends StatelessWidget { const Header({ Key key, diff --git a/example/lib/examples/mix_slivers.dart b/example/lib/examples/mix_slivers.dart new file mode 100644 index 0000000..698c352 --- /dev/null +++ b/example/lib/examples/mix_slivers.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_sticky_header/flutter_sticky_header.dart'; + +import '../common.dart'; + +class MixSliversExample extends StatelessWidget { + const MixSliversExample({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return AppScaffold( + title: 'List Example', + slivers: [ + SliverAppBar( + backgroundColor: Colors.orange, + title: Text('SliverAppBar'), + automaticallyImplyLeading: false, + pinned: true, + ), + SliverToBoxAdapter( + child: Container( + height: 50, + color: Colors.red, + ), + ), + _StickyHeaderList(index: 0), + _StickyHeaderList(index: 1), + _StickyHeaderList(index: 2), + _StickyHeaderList(index: 3), + ], + ); + } +} + +class _StickyHeaderList extends StatelessWidget { + const _StickyHeaderList({ + Key key, + this.index, + }) : super(key: key); + + final int index; + + @override + Widget build(BuildContext context) { + return SliverStickyHeader( + header: Header(index: index), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, i) => ListTile( + leading: CircleAvatar( + child: Text('$index'), + ), + title: Text('List tile #$i'), + ), + childCount: 6, + ), + ), + ); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index 71b9818..2a10c11 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -6,6 +6,7 @@ import 'examples/not_sticky.dart'; import 'examples/side_header.dart'; import 'examples/animated_header.dart'; import 'examples/reverse.dart'; +import 'examples/mix_slivers.dart'; void main() => runApp(const App()); @@ -60,6 +61,10 @@ class _Home extends StatelessWidget { text: 'Reverse List Example', builder: (_) => const ReverseExample(), ), + _Item( + text: 'Mixing other slivers', + builder: (_) => const MixSliversExample(), + ), ], ), ); diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index 74f5389..aa90fea 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -1,6 +1,7 @@ import 'dart:math' as math; import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart'; import 'package:flutter_sticky_header/src/rendering/sticky_header_constraints.dart'; @@ -16,10 +17,12 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { RenderSliver child, bool overlapsContent: false, bool sticky: true, + StickyHeaderController controller, }) : assert(overlapsContent != null), assert(sticky != null), _overlapsContent = overlapsContent, - _sticky = sticky { + _sticky = sticky, + _controller = controller { this.header = header; this.child = child; } @@ -46,6 +49,17 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { markNeedsLayout(); } + StickyHeaderController get controller => _controller; + StickyHeaderController _controller; + set controller(StickyHeaderController value) { + if (_controller == value) return; + if (_controller != null && value != null) { + // We copy the state of the old controller. + value.stickyHeaderScrollOffset = _controller.stickyHeaderScrollOffset; + } + _controller = value; + } + /// The render object's header RenderBox get header => _header; RenderBox _header; @@ -218,11 +232,16 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { _isPinned = sticky && ((constraints.scrollOffset + constraints.overlap) > 0.0 || constraints.remainingPaintExtent == constraints.viewportMainAxisExtent); + + final double headerScrollRatio = ((headerPosition - constraints.overlap).abs() / _headerExtent); + if (_isPinned && headerScrollRatio <= 1) { + controller?.stickyHeaderScrollOffset = constraints.precedingScrollExtent; + } // second layout if scroll percentage changed and header is a RenderStickyHeaderLayoutBuilder. if (header is RenderStickyHeaderLayoutBuilder) { - double scrollPercentage = ((headerPosition - constraints.overlap).abs() / _headerExtent).clamp(0.0, 1.0); + double headerScrollRatioClamped = headerScrollRatio.clamp(0.0, 1.0); - SliverStickyHeaderState state = SliverStickyHeaderState(scrollPercentage, _isPinned); + SliverStickyHeaderState state = SliverStickyHeaderState(headerScrollRatioClamped, _isPinned); if (_oldState != state) { _oldState = state; header.layout( diff --git a/lib/src/widgets/sliver_sticky_header.dart b/lib/src/widgets/sliver_sticky_header.dart index ec588f7..041adbb 100644 --- a/lib/src/widgets/sliver_sticky_header.dart +++ b/lib/src/widgets/sliver_sticky_header.dart @@ -9,6 +9,101 @@ typedef Widget SliverStickyHeaderWidgetBuilder( SliverStickyHeaderState state, ); +/// A +class StickyHeaderController with ChangeNotifier { + /// The offset to use in order to jump to the first item + /// of current the sticky header. + /// + /// If there is no sticky headers, this is 0. + double get stickyHeaderScrollOffset => _stickyHeaderScrollOffset; + double _stickyHeaderScrollOffset = 0; + + /// This setter should only be used by flutter_sticky_header package. + set stickyHeaderScrollOffset(double value) { + assert(value != null); + if (_stickyHeaderScrollOffset != value) { + _stickyHeaderScrollOffset = value; + notifyListeners(); + } + } +} + +/// The [StickyHeaderController] for descendant widgets that don't specify one +/// explicitly. +/// +/// [DefaultStickyHeaderController] is an inherited widget that is used to share a +/// [StickyHeaderController] with [SliverStickyHeader]s. It's used when sharing an +/// explicitly created [StickyHeaderController] isn't convenient because the sticky +/// headers are created by a stateless parent widget or by different parent +/// widgets. +class DefaultStickyHeaderController extends StatefulWidget { + const DefaultStickyHeaderController({ + Key key, + @required this.child, + }) : super(key: key); + + /// The widget below this widget in the tree. + /// + /// Typically a [Scaffold] whose [AppBar] includes a [TabBar]. + /// + /// {@macro flutter.widgets.child} + final Widget child; + + /// The closest instance of this class that encloses the given context. + /// + /// Typical usage: + /// + /// ```dart + /// StickyHeaderController controller = DefaultStickyHeaderController.of(context); + /// ``` + static StickyHeaderController of(BuildContext context) { + final _StickyHeaderControllerScope scope = context.dependOnInheritedWidgetOfExactType<_StickyHeaderControllerScope>(); + return scope?.controller; + } + + @override + _DefaultStickyHeaderControllerState createState() => _DefaultStickyHeaderControllerState(); +} + +class _DefaultStickyHeaderControllerState extends State { + StickyHeaderController _controller; + + @override + void initState() { + super.initState(); + _controller = StickyHeaderController(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return _StickyHeaderControllerScope( + controller: _controller, + child: widget.child, + ); + } +} + +class _StickyHeaderControllerScope extends InheritedWidget { + const _StickyHeaderControllerScope({ + Key key, + this.controller, + Widget child, + }) : super(key: key, child: child); + + final StickyHeaderController controller; + + @override + bool updateShouldNotify(_StickyHeaderControllerScope old) { + return controller != old.controller; + } +} + /// State describing how a sticky header is rendered. @immutable class SliverStickyHeaderState { @@ -46,12 +141,16 @@ class SliverStickyHeader extends RenderObjectWidget { /// the [sliver] scrolls off the viewport. /// /// The [overlapsContent] and [sticky] arguments must not be null. + /// + /// If a [StickyHeaderController] is not provided, then the value of [DefaultStickyHeaderController.of] + /// will be used. SliverStickyHeader({ Key key, this.header, this.sliver, this.overlapsContent: false, this.sticky = true, + this.controller, }) : assert(overlapsContent != null), assert(sticky != null), super(key: key); @@ -70,11 +169,18 @@ class SliverStickyHeader extends RenderObjectWidget { /// Defaults to true. final bool sticky; + /// The controller used to interact with this sliver. + /// + /// If a [StickyHeaderController] is not provided, then the value of [DefaultStickyHeaderController.of] + /// will be used. + final StickyHeaderController controller; + @override RenderSliverStickyHeader createRenderObject(BuildContext context) { return RenderSliverStickyHeader( overlapsContent: overlapsContent, sticky: sticky, + controller: controller ?? DefaultStickyHeaderController.of(context), ); } @@ -85,7 +191,8 @@ class SliverStickyHeader extends RenderObjectWidget { void updateRenderObject(BuildContext context, RenderSliverStickyHeader renderObject) { renderObject ..overlapsContent = overlapsContent - ..sticky = sticky; + ..sticky = sticky + ..controller = controller ?? DefaultStickyHeaderController.of(context); } } @@ -100,12 +207,16 @@ class SliverStickyHeaderBuilder extends StatelessWidget { /// each time its scroll percentage changes. /// /// The [builder], [overlapsContent] and [sticky] arguments must not be null. + /// + /// If a [StickyHeaderController] is not provided, then the value of [DefaultStickyHeaderController.of] + /// will be used. const SliverStickyHeaderBuilder({ Key key, @required this.builder, this.sliver, this.overlapsContent: false, this.sticky = true, + this.controller, }) : assert(builder != null), assert(overlapsContent != null), assert(sticky != null), @@ -128,12 +239,19 @@ class SliverStickyHeaderBuilder extends StatelessWidget { /// Defaults to true. final bool sticky; + /// The controller used to interact with this sliver. + /// + /// If a [StickyHeaderController] is not provided, then the value of [DefaultStickyHeaderController.of] + /// will be used. + final StickyHeaderController controller; + @override Widget build(BuildContext context) { return SliverStickyHeader( overlapsContent: overlapsContent, sliver: sliver, sticky: sticky, + controller: controller, header: StickyHeaderLayoutBuilder( builder: (context, constraints) => builder(context, constraints.state), ), diff --git a/pubspec.yaml b/pubspec.yaml index d2d33bd..56be8eb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_sticky_header description: Flutter implementation of sticky headers as a sliver. Use it in a CustomScrollView. -version: 0.4.1 +version: 0.4.2 homepage: https://github.com/letsar/flutter_sticky_header dependencies: diff --git a/test/controller_test.dart b/test/controller_test.dart new file mode 100644 index 0000000..d5b218d --- /dev/null +++ b/test/controller_test.dart @@ -0,0 +1,234 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:flutter_sticky_header/flutter_sticky_header.dart'; + +void main() { + setUp(() { + WidgetsBinding.instance.renderView.configuration = TestViewConfiguration(size: Size(400, 800)); + }); + + testWidgets('StickyHeaderController.stickyHeaderScrollOffset', (WidgetTester tester) async { + final StickyHeaderController stickyHeaderController = StickyHeaderController(); + final ScrollController scrollController = ScrollController(); + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: CustomScrollView( + cacheExtent: 0, + controller: scrollController, + slivers: [ + SliverStickyHeader( + header: _Header(index: 0), + sliver: const _Sliver(), + controller: stickyHeaderController, + ), + SliverStickyHeader( + header: _Header(index: 1), + sliver: const _Sliver(), + controller: stickyHeaderController, + ), + SliverStickyHeader( + header: _Header(index: 2), + sliver: const _Sliver(), + controller: stickyHeaderController, + ), + ], + ), + ), + ), + ); + + final header00Finder = find.text('Header #0'); + final header01Finder = find.text('Header #1'); + final header02Finder = find.text('Header #2'); + + expect(header00Finder, findsOneWidget); + expect(header01Finder, findsNothing); + expect(header02Finder, findsNothing); + expect(stickyHeaderController.stickyHeaderScrollOffset, equals(0)); + + final gesture = await tester.startGesture(Offset(200, 100)); + + // We scroll just before the Header #1. + await gesture.moveBy(Offset(0, -80)); + await tester.pump(); + + expect(header00Finder, findsOneWidget); + expect(header01Finder, findsNothing); + expect(header02Finder, findsNothing); + expect(stickyHeaderController.stickyHeaderScrollOffset, equals(0)); + + // We scroll just after the Header #1 so that it is visible. + await gesture.moveBy(Offset(0, -80)); + await tester.pump(); + + expect(header00Finder, findsOneWidget); + expect(header01Finder, findsOneWidget); + expect(header02Finder, findsNothing); + expect(stickyHeaderController.stickyHeaderScrollOffset, equals(0)); + + // We scroll in a way that Headers 0 and 1 are side by side. + await gesture.moveBy(Offset(0, -640)); + await tester.pump(); + + expect(header00Finder, findsOneWidget); + expect(header01Finder, findsOneWidget); + expect(header02Finder, findsNothing); + expect(stickyHeaderController.stickyHeaderScrollOffset, equals(0)); + + // We scroll in a way that Header #1 is at the top of the screen. + await gesture.moveBy(Offset(0, -80)); + await tester.pump(); + + expect(header00Finder, findsNothing); + expect(header01Finder, findsOneWidget); + expect(header02Finder, findsNothing); + expect(stickyHeaderController.stickyHeaderScrollOffset, equals(880)); + + // We scroll in a way that Header #1 is not visible. + await gesture.moveBy(Offset(0, -80)); + await tester.pump(); + + expect(header00Finder, findsNothing); + expect(header01Finder, findsOneWidget); + expect(header02Finder, findsNothing); + expect(stickyHeaderController.stickyHeaderScrollOffset, equals(880)); + }); + + testWidgets('StickyHeaderController.stickyHeaderScrollOffset - reverse', (WidgetTester tester) async { + final StickyHeaderController stickyHeaderController = StickyHeaderController(); + final ScrollController scrollController = ScrollController(); + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: CustomScrollView( + controller: scrollController, + cacheExtent: 0, + reverse: true, + slivers: [ + SliverStickyHeader( + header: _Header(index: 0), + sliver: const _Sliver(), + controller: stickyHeaderController, + ), + SliverStickyHeader( + header: _Header(index: 1), + sliver: const _Sliver(), + controller: stickyHeaderController, + ), + SliverStickyHeader( + header: _Header(index: 2), + sliver: const _Sliver(), + controller: stickyHeaderController, + ), + ], + ), + ), + ), + ); + + final header00Finder = find.text('Header #0'); + final header01Finder = find.text('Header #1'); + final header02Finder = find.text('Header #2'); + + expect(header00Finder, findsOneWidget); + expect(header01Finder, findsNothing); + expect(header02Finder, findsNothing); + expect(stickyHeaderController.stickyHeaderScrollOffset, equals(0)); + + final gesture = await tester.startGesture(Offset(200, 100)); + + // We scroll just before the Header #1. + await gesture.moveBy(Offset(0, 80)); + await tester.pump(); + + expect(header00Finder, findsOneWidget); + expect(header01Finder, findsNothing); + expect(header02Finder, findsNothing); + expect(stickyHeaderController.stickyHeaderScrollOffset, equals(0)); + + // We scroll just after the Header #1 so that it is visible. + await gesture.moveBy(Offset(0, 80)); + await tester.pump(); + + expect(header00Finder, findsOneWidget); + expect(header01Finder, findsOneWidget); + expect(header02Finder, findsNothing); + expect(stickyHeaderController.stickyHeaderScrollOffset, equals(0)); + + // We scroll in a way that Headers 0 and 1 are side by side. + await gesture.moveBy(Offset(0, 640)); + await tester.pump(); + + expect(header00Finder, findsOneWidget); + expect(header01Finder, findsOneWidget); + expect(header02Finder, findsNothing); + expect(stickyHeaderController.stickyHeaderScrollOffset, equals(0)); + + // We scroll in a way that Header #1 is at the top of the screen. + await gesture.moveBy(Offset(0, 80)); + await tester.pump(); + + expect(header00Finder, findsNothing); + expect(header01Finder, findsOneWidget); + expect(header02Finder, findsNothing); + expect(stickyHeaderController.stickyHeaderScrollOffset, equals(880)); + + // We scroll in a way that Header #1 is no longer visible. + await gesture.moveBy(Offset(0, 80)); + await tester.pump(); + + expect(header00Finder, findsNothing); + expect(header01Finder, findsOneWidget); + expect(header02Finder, findsNothing); + expect(stickyHeaderController.stickyHeaderScrollOffset, equals(880)); + }); +} + +class _Header extends StatelessWidget { + const _Header({ + Key key, + @required this.index, + }) : super(key: key); + + final int index; + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.blue, + child: Text('Header #$index'), + height: 80, + ); + } +} + +class _Sliver extends StatelessWidget { + const _Sliver({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return SliverList( + delegate: SliverChildBuilderDelegate( + (context, i) => const _SliverItem(), + childCount: 20, + ), + ); + } +} + +class _SliverItem extends StatelessWidget { + const _SliverItem({ + Key key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox(height: 40); + } +} diff --git a/test/flutter_sticky_header_test.dart b/test/sticky_test.dart similarity index 83% rename from test/flutter_sticky_header_test.dart rename to test/sticky_test.dart index 7d2339e..28f2eb2 100644 --- a/test/flutter_sticky_header_test.dart +++ b/test/sticky_test.dart @@ -10,27 +10,25 @@ void main() { testWidgets('Mix sticky and not sticky headers', (WidgetTester tester) async { await tester.pumpWidget( - RepaintBoundary( - child: MaterialApp( - home: Scaffold( - body: CustomScrollView( - cacheExtent: 0, - slivers: [ - SliverStickyHeader( - header: _Header(index: 0), - sliver: const _Sliver(), - ), - SliverStickyHeader( - header: _Header(index: 1), - sticky: false, - sliver: const _Sliver(), - ), - SliverStickyHeader( - header: _Header(index: 2), - sliver: const _Sliver(), - ), - ], - ), + MaterialApp( + home: Scaffold( + body: CustomScrollView( + cacheExtent: 0, + slivers: [ + SliverStickyHeader( + header: _Header(index: 0), + sliver: const _Sliver(), + ), + SliverStickyHeader( + header: _Header(index: 1), + sticky: false, + sliver: const _Sliver(), + ), + SliverStickyHeader( + header: _Header(index: 2), + sliver: const _Sliver(), + ), + ], ), ), ), @@ -212,20 +210,3 @@ class _SliverItem extends StatelessWidget { return SizedBox(height: 40); } } - -// class _IsOnScreen extends Matcher { -// const _IsOnScreen(); - -// @override -// bool matches(covariant Finder finder, Map matchState) { -// finder.ev -// return _hasAncestorMatching(finder, (Widget widget) { -// if (widget is Offstage) -// return widget.offstage; -// return false; -// }); -// } - -// @override -// Description describe(Description description) => description.add('onscreen'); -// } From e388fb4d72d2564e05755ba11e060ce7a5ee53d7 Mon Sep 17 00:00:00 2001 From: Romain Rastel Date: Thu, 4 Jun 2020 17:39:25 +0200 Subject: [PATCH 08/30] Create FUNDING.yml --- .github/FUNDING.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..030b991 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,10 @@ +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: romainrastel +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: ['https://www.buymeacoffee.com/romainrastel', 'paypal.me/RomainRastel'] From d24aa6cdf4c5a2050aa322e58d49499a0a1e4811 Mon Sep 17 00:00:00 2001 From: Romain Rastel Date: Thu, 25 Jun 2020 19:45:48 +0200 Subject: [PATCH 09/30] Update FUNDING.yml --- .github/FUNDING.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 030b991..c7f0c06 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,4 +1,4 @@ -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +github: letsar patreon: romainrastel open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username From e9ee3a0181b415bda68d0ef505c65f9ed00dda36 Mon Sep 17 00:00:00 2001 From: Romain Rastel Date: Tue, 21 Jul 2020 14:59:53 +0200 Subject: [PATCH 10/30] Fixes static analysis issues --- CHANGELOG.md | 4 +++ README.md | 2 +- example/lib/examples/side_header.dart | 4 +-- lib/src/widgets/sliver_sticky_header.dart | 22 ++++++++++------ .../widgets/sticky_header_layout_builder.dart | 25 +++++++++++++------ pubspec.yaml | 4 +-- 6 files changed, 41 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf445dd..82ac559 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.3 +### Fixed +* Static analysis issues. + ## 0.4.2 ### Added * A StickyHeaderController to get the scroll offset of the current sticky header. diff --git a/README.md b/README.md index b296f11..e57876b 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ In the `pubspec.yaml` of your flutter project, add the following dependency: ```yaml dependencies: ... - flutter_sticky_header: "^0.4.2" + flutter_sticky_header: "^0.4.3" ``` In your library add the following import: diff --git a/example/lib/examples/side_header.dart b/example/lib/examples/side_header.dart index 3fd77e9..8c6074e 100644 --- a/example/lib/examples/side_header.dart +++ b/example/lib/examples/side_header.dart @@ -38,7 +38,8 @@ class _StickyHeaderGrid extends StatelessWidget { sliver: SliverPadding( padding: const EdgeInsets.only(left: 60), sliver: SliverGrid( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, crossAxisSpacing: 4.0, mainAxisSpacing: 4.0), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, crossAxisSpacing: 4.0, mainAxisSpacing: 4.0), delegate: SliverChildBuilderDelegate( (context, i) => GridTile( child: Card( @@ -90,6 +91,5 @@ class _SideHeader extends StatelessWidget { ), ), ); - ; } } diff --git a/lib/src/widgets/sliver_sticky_header.dart b/lib/src/widgets/sliver_sticky_header.dart index 041adbb..aaff895 100644 --- a/lib/src/widgets/sliver_sticky_header.dart +++ b/lib/src/widgets/sliver_sticky_header.dart @@ -57,15 +57,18 @@ class DefaultStickyHeaderController extends StatefulWidget { /// StickyHeaderController controller = DefaultStickyHeaderController.of(context); /// ``` static StickyHeaderController of(BuildContext context) { - final _StickyHeaderControllerScope scope = context.dependOnInheritedWidgetOfExactType<_StickyHeaderControllerScope>(); + final _StickyHeaderControllerScope scope = context + .dependOnInheritedWidgetOfExactType<_StickyHeaderControllerScope>(); return scope?.controller; } @override - _DefaultStickyHeaderControllerState createState() => _DefaultStickyHeaderControllerState(); + _DefaultStickyHeaderControllerState createState() => + _DefaultStickyHeaderControllerState(); } -class _DefaultStickyHeaderControllerState extends State { +class _DefaultStickyHeaderControllerState + extends State { StickyHeaderController _controller; @override @@ -122,7 +125,8 @@ class SliverStickyHeaderState { if (identical(this, other)) return true; if (other is! SliverStickyHeaderState) return false; final SliverStickyHeaderState typedOther = other; - return scrollPercentage == typedOther.scrollPercentage && isPinned == typedOther.isPinned; + return scrollPercentage == typedOther.scrollPercentage && + isPinned == typedOther.isPinned; } @override @@ -185,10 +189,12 @@ class SliverStickyHeader extends RenderObjectWidget { } @override - SliverStickyHeaderRenderObjectElement createElement() => SliverStickyHeaderRenderObjectElement(this); + SliverStickyHeaderRenderObjectElement createElement() => + SliverStickyHeaderRenderObjectElement(this); @override - void updateRenderObject(BuildContext context, RenderSliverStickyHeader renderObject) { + void updateRenderObject( + BuildContext context, RenderSliverStickyHeader renderObject) { renderObject ..overlapsContent = overlapsContent ..sticky = sticky @@ -261,7 +267,8 @@ class SliverStickyHeaderBuilder extends StatelessWidget { class SliverStickyHeaderRenderObjectElement extends RenderObjectElement { /// Creates an element that uses the given widget as its configuration. - SliverStickyHeaderRenderObjectElement(SliverStickyHeader widget) : super(widget); + SliverStickyHeaderRenderObjectElement(SliverStickyHeader widget) + : super(widget); @override SliverStickyHeader get widget => super.widget; @@ -280,6 +287,7 @@ class SliverStickyHeaderRenderObjectElement extends RenderObjectElement { void forgetChild(Element child) { if (child == _header) _header = null; if (child == _sliver) _sliver = null; + super.forgetChild(child); } @override diff --git a/lib/src/widgets/sticky_header_layout_builder.dart b/lib/src/widgets/sticky_header_layout_builder.dart index 9d95cda..2de29ab 100644 --- a/lib/src/widgets/sticky_header_layout_builder.dart +++ b/lib/src/widgets/sticky_header_layout_builder.dart @@ -3,7 +3,8 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_sticky_header/src/rendering/sticky_header_constraints.dart'; import 'package:flutter_sticky_header/src/rendering/sticky_header_layout_builder.dart'; -typedef Widget StickyHeaderLayoutWidgetBuilder(BuildContext context, StickyHeaderConstraints constraints); +typedef Widget StickyHeaderLayoutWidgetBuilder( + BuildContext context, StickyHeaderConstraints constraints); /// Builds a widget tree that can depend on the [StickyHeaderConstraints]. /// @@ -24,16 +25,19 @@ class StickyHeaderLayoutBuilder extends RenderObjectWidget { final StickyHeaderLayoutWidgetBuilder builder; @override - RenderObjectElement createElement() => _StickyHeaderLayoutBuilderElement(this); + RenderObjectElement createElement() => + _StickyHeaderLayoutBuilderElement(this); @override - RenderObject createRenderObject(BuildContext context) => RenderStickyHeaderLayoutBuilder(); + RenderObject createRenderObject(BuildContext context) => + RenderStickyHeaderLayoutBuilder(); // updateRenderObject is redundant with the logic in the _StickyHeaderLayoutBuilderElement below. } class _StickyHeaderLayoutBuilderElement extends RenderObjectElement { - _StickyHeaderLayoutBuilderElement(StickyHeaderLayoutBuilder widget) : super(widget); + _StickyHeaderLayoutBuilderElement(StickyHeaderLayoutBuilder widget) + : super(widget); @override StickyHeaderLayoutBuilder get widget => super.widget; @@ -52,6 +56,7 @@ class _StickyHeaderLayoutBuilderElement extends RenderObjectElement { void forgetChild(Element child) { assert(child == _child); _child = null; + super.forgetChild(child); } @override @@ -74,7 +79,8 @@ class _StickyHeaderLayoutBuilderElement extends RenderObjectElement { // This gets called if markNeedsBuild() is called on us. // That might happen if, e.g., our builder uses Inherited widgets. renderObject.markNeedsLayout(); - super.performRebuild(); // Calls widget.updateRenderObject (a no-op in this case). + super + .performRebuild(); // Calls widget.updateRenderObject (a no-op in this case). } @override @@ -91,14 +97,16 @@ class _StickyHeaderLayoutBuilderElement extends RenderObjectElement { built = widget.builder(this, constraints); debugWidgetBuilderValue(widget, built); } catch (e, stack) { - built = ErrorWidget.builder(_debugReportException('building $widget', e, stack)); + built = ErrorWidget.builder( + _debugReportException('building $widget', e, stack)); } } try { _child = updateChild(_child, built, null); assert(_child != null); } catch (e, stack) { - built = ErrorWidget.builder(_debugReportException('building $widget', e, stack)); + built = ErrorWidget.builder( + _debugReportException('building $widget', e, stack)); _child = updateChild(null, built, slot); } }); @@ -106,7 +114,8 @@ class _StickyHeaderLayoutBuilderElement extends RenderObjectElement { @override void insertChildRenderObject(RenderObject child, slot) { - final RenderObjectWithChildMixin renderObject = this.renderObject; + final RenderObjectWithChildMixin renderObject = + this.renderObject; assert(slot == null); assert(renderObject.debugValidateChild(child)); renderObject.child = child; diff --git a/pubspec.yaml b/pubspec.yaml index 56be8eb..a9c0cf1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_sticky_header description: Flutter implementation of sticky headers as a sliver. Use it in a CustomScrollView. -version: 0.4.2 +version: 0.4.3 homepage: https://github.com/letsar/flutter_sticky_header dependencies: @@ -13,4 +13,4 @@ dev_dependencies: environment: sdk: ">=2.2.0 <3.0.0" - flutter: ">=1.5.9-pre.94 <2.0.0" + flutter: ">=1.12.0 <2.0.0" From c6676d25fe9f3dde47ebaa429db589e877e10e38 Mon Sep 17 00:00:00 2001 From: Romain Rastel Date: Tue, 21 Jul 2020 23:50:51 +0200 Subject: [PATCH 11/30] Formatting --- example/lib/common.dart | 3 +- example/lib/examples/grid.dart | 3 +- example/lib/main.dart | 6 +- lib/src/rendering/sliver_sticky_header.dart | 92 +++++++++++++------ .../sticky_header_layout_builder.dart | 6 +- test/controller_test.dart | 15 ++- test/sticky_test.dart | 9 +- 7 files changed, 94 insertions(+), 40 deletions(-) diff --git a/example/lib/common.dart b/example/lib/common.dart index a8b49a2..046d93e 100644 --- a/example/lib/common.dart +++ b/example/lib/common.dart @@ -41,7 +41,8 @@ class _FloatingActionButton extends StatelessWidget { child: Icon(Icons.adjust), backgroundColor: Colors.green, onPressed: () { - final double offset = DefaultStickyHeaderController.of(context).stickyHeaderScrollOffset; + final double offset = + DefaultStickyHeaderController.of(context).stickyHeaderScrollOffset; PrimaryScrollController.of(context).animateTo( offset, duration: Duration(milliseconds: 300), diff --git a/example/lib/examples/grid.dart b/example/lib/examples/grid.dart index f68e3d5..2b2312f 100644 --- a/example/lib/examples/grid.dart +++ b/example/lib/examples/grid.dart @@ -35,7 +35,8 @@ class _StickyHeaderGrid extends StatelessWidget { return SliverStickyHeader( header: Header(index: index), sliver: SliverGrid( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, crossAxisSpacing: 4.0, mainAxisSpacing: 4.0), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, crossAxisSpacing: 4.0, mainAxisSpacing: 4.0), delegate: SliverChildBuilderDelegate( (context, i) => GridTile( child: Card( diff --git a/example/lib/main.dart b/example/lib/main.dart index 2a10c11..a2cdd50 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -86,12 +86,14 @@ class _Item extends StatelessWidget { return Card( color: Colors.blue, child: InkWell( - onTap: () => Navigator.push(context, MaterialPageRoute(builder: builder)), + onTap: () => + Navigator.push(context, MaterialPageRoute(builder: builder)), child: Container( padding: EdgeInsets.all(16), child: Text( text, - style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 20), + style: TextStyle( + color: Colors.white, fontWeight: FontWeight.bold, fontSize: 20), ), ), ), diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index aa90fea..329dc71 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -80,7 +80,8 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { @override void setupParentData(RenderObject child) { - if (child.parentData is! SliverPhysicalParentData) child.parentData = SliverPhysicalParentData(); + if (child.parentData is! SliverPhysicalParentData) + child.parentData = SliverPhysicalParentData(); } @override @@ -144,7 +145,8 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { } // One of them is not null. - AxisDirection axisDirection = applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection); + AxisDirection axisDirection = applyGrowthDirectionToAxisDirection( + constraints.axisDirection, constraints.growthDirection); if (header != null) { header.layout( @@ -159,8 +161,10 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { // Compute the header extent only one time. double headerExtent = headerLogicalExtent; - final double headerPaintExtent = calculatePaintOffset(constraints, from: 0.0, to: headerExtent); - final double headerCacheExtent = calculateCacheOffset(constraints, from: 0.0, to: headerExtent); + final double headerPaintExtent = + calculatePaintOffset(constraints, from: 0.0, to: headerExtent); + final double headerCacheExtent = + calculateCacheOffset(constraints, from: 0.0, to: headerExtent); if (child == null) { geometry = SliverGeometry( @@ -169,15 +173,18 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { paintExtent: headerPaintExtent, cacheExtent: headerCacheExtent, hitTestExtent: headerPaintExtent, - hasVisualOverflow: headerExtent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0); + hasVisualOverflow: headerExtent > constraints.remainingPaintExtent || + constraints.scrollOffset > 0.0); } else { child.layout( constraints.copyWith( scrollOffset: math.max(0.0, constraints.scrollOffset - headerExtent), cacheOrigin: math.min(0.0, constraints.cacheOrigin + headerExtent), overlap: 0.0, - remainingPaintExtent: constraints.remainingPaintExtent - headerPaintExtent, - remainingCacheExtent: constraints.remainingCacheExtent - headerCacheExtent, + remainingPaintExtent: + constraints.remainingPaintExtent - headerPaintExtent, + remainingCacheExtent: + constraints.remainingCacheExtent - headerCacheExtent, ), parentUsesSize: true, ); @@ -190,17 +197,24 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { } final double paintExtent = math.min( - headerPaintExtent + math.max(childLayoutGeometry.paintExtent, childLayoutGeometry.layoutExtent), + headerPaintExtent + + math.max(childLayoutGeometry.paintExtent, + childLayoutGeometry.layoutExtent), constraints.remainingPaintExtent, ); geometry = SliverGeometry( scrollExtent: headerExtent + childLayoutGeometry.scrollExtent, paintExtent: paintExtent, - layoutExtent: math.min(headerPaintExtent + childLayoutGeometry.layoutExtent, paintExtent), - cacheExtent: math.min(headerCacheExtent + childLayoutGeometry.cacheExtent, constraints.remainingCacheExtent), + layoutExtent: math.min( + headerPaintExtent + childLayoutGeometry.layoutExtent, paintExtent), + cacheExtent: math.min( + headerCacheExtent + childLayoutGeometry.cacheExtent, + constraints.remainingCacheExtent), maxPaintExtent: headerExtent + childLayoutGeometry.maxPaintExtent, - hitTestExtent: math.max(headerPaintExtent + childLayoutGeometry.paintExtent, headerPaintExtent + childLayoutGeometry.hitTestExtent), + hitTestExtent: math.max( + headerPaintExtent + childLayoutGeometry.paintExtent, + headerPaintExtent + childLayoutGeometry.hitTestExtent), hasVisualOverflow: childLayoutGeometry.hasVisualOverflow, ); @@ -212,10 +226,13 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { childParentData.paintOffset = Offset.zero; break; case AxisDirection.right: - childParentData.paintOffset = Offset(calculatePaintOffset(constraints, from: 0.0, to: headerExtent), 0.0); + childParentData.paintOffset = Offset( + calculatePaintOffset(constraints, from: 0.0, to: headerExtent), + 0.0); break; case AxisDirection.down: - childParentData.paintOffset = Offset(0.0, calculatePaintOffset(constraints, from: 0.0, to: headerExtent)); + childParentData.paintOffset = Offset(0.0, + calculatePaintOffset(constraints, from: 0.0, to: headerExtent)); break; case AxisDirection.left: childParentData.paintOffset = Offset.zero; @@ -227,21 +244,30 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { final SliverPhysicalParentData headerParentData = header.parentData; final double childScrollExtent = child?.geometry?.scrollExtent ?? 0.0; final double headerPosition = sticky - ? math.min(constraints.overlap, childScrollExtent - constraints.scrollOffset - (overlapsContent ? _headerExtent : 0.0)) + ? math.min( + constraints.overlap, + childScrollExtent - + constraints.scrollOffset - + (overlapsContent ? _headerExtent : 0.0)) : -constraints.scrollOffset; _isPinned = sticky && - ((constraints.scrollOffset + constraints.overlap) > 0.0 || constraints.remainingPaintExtent == constraints.viewportMainAxisExtent); + ((constraints.scrollOffset + constraints.overlap) > 0.0 || + constraints.remainingPaintExtent == + constraints.viewportMainAxisExtent); - final double headerScrollRatio = ((headerPosition - constraints.overlap).abs() / _headerExtent); + final double headerScrollRatio = + ((headerPosition - constraints.overlap).abs() / _headerExtent); if (_isPinned && headerScrollRatio <= 1) { - controller?.stickyHeaderScrollOffset = constraints.precedingScrollExtent; + controller?.stickyHeaderScrollOffset = + constraints.precedingScrollExtent; } // second layout if scroll percentage changed and header is a RenderStickyHeaderLayoutBuilder. if (header is RenderStickyHeaderLayoutBuilder) { double headerScrollRatioClamped = headerScrollRatio.clamp(0.0, 1.0); - SliverStickyHeaderState state = SliverStickyHeaderState(headerScrollRatioClamped, _isPinned); + SliverStickyHeaderState state = + SliverStickyHeaderState(headerScrollRatioClamped, _isPinned); if (_oldState != state) { _oldState = state; header.layout( @@ -256,13 +282,15 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { switch (axisDirection) { case AxisDirection.up: - headerParentData.paintOffset = Offset(0.0, geometry.paintExtent - headerPosition - _headerExtent); + headerParentData.paintOffset = Offset( + 0.0, geometry.paintExtent - headerPosition - _headerExtent); break; case AxisDirection.down: headerParentData.paintOffset = Offset(0.0, headerPosition); break; case AxisDirection.left: - headerParentData.paintOffset = Offset(geometry.paintExtent - headerPosition - _headerExtent, 0.0); + headerParentData.paintOffset = Offset( + geometry.paintExtent - headerPosition - _headerExtent, 0.0); break; case AxisDirection.right: headerParentData.paintOffset = Offset(headerPosition, 0.0); @@ -272,9 +300,11 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { } @override - bool hitTestChildren(SliverHitTestResult result, {@required double mainAxisPosition, @required double crossAxisPosition}) { + bool hitTestChildren(SliverHitTestResult result, + {@required double mainAxisPosition, @required double crossAxisPosition}) { assert(geometry.hitTestExtent > 0.0); - if (header != null && mainAxisPosition - constraints.overlap <= _headerExtent) { + if (header != null && + mainAxisPosition - constraints.overlap <= _headerExtent) { return hitTestBoxChild( BoxHitTestResult.wrap(SliverHitTestResult.wrap(result)), header, @@ -284,17 +314,27 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { (_overlapsContent && child != null && child.geometry.hitTestExtent > 0.0 && - child.hitTest(result, mainAxisPosition: mainAxisPosition - childMainAxisPosition(child), crossAxisPosition: crossAxisPosition)); + child.hitTest(result, + mainAxisPosition: + mainAxisPosition - childMainAxisPosition(child), + crossAxisPosition: crossAxisPosition)); } else if (child != null && child.geometry.hitTestExtent > 0.0) { - return child.hitTest(result, mainAxisPosition: mainAxisPosition - childMainAxisPosition(child), crossAxisPosition: crossAxisPosition); + return child.hitTest(result, + mainAxisPosition: mainAxisPosition - childMainAxisPosition(child), + crossAxisPosition: crossAxisPosition); } return false; } @override double childMainAxisPosition(RenderObject child) { - if (child == header) return _isPinned ? 0.0 : -(constraints.scrollOffset + constraints.overlap); - if (child == this.child) return calculatePaintOffset(constraints, from: 0.0, to: headerLogicalExtent); + if (child == header) + return _isPinned + ? 0.0 + : -(constraints.scrollOffset + constraints.overlap); + if (child == this.child) + return calculatePaintOffset(constraints, + from: 0.0, to: headerLogicalExtent); return null; } diff --git a/lib/src/rendering/sticky_header_layout_builder.dart b/lib/src/rendering/sticky_header_layout_builder.dart index a082ce1..2d5cc44 100644 --- a/lib/src/rendering/sticky_header_layout_builder.dart +++ b/lib/src/rendering/sticky_header_layout_builder.dart @@ -2,7 +2,8 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_sticky_header/src/rendering/sticky_header_constraints.dart'; -class RenderStickyHeaderLayoutBuilder extends RenderBox with RenderObjectWithChildMixin { +class RenderStickyHeaderLayoutBuilder extends RenderBox + with RenderObjectWithChildMixin { RenderStickyHeaderLayoutBuilder({ LayoutCallback callback, }) : _callback = callback; @@ -22,7 +23,8 @@ class RenderStickyHeaderLayoutBuilder extends RenderBox with RenderObjectWithChi bool _debugThrowIfNotCheckingIntrinsics() { assert(() { if (!RenderObject.debugCheckingIntrinsics) { - throw FlutterError('StickyHeaderLayoutBuilder does not support returning intrinsic dimensions.\n' + throw FlutterError( + 'StickyHeaderLayoutBuilder does not support returning intrinsic dimensions.\n' 'Calculating the intrinsic dimensions would require running the layout ' 'callback speculatively, which might mutate the live render object tree.'); } diff --git a/test/controller_test.dart b/test/controller_test.dart index d5b218d..fbca4f0 100644 --- a/test/controller_test.dart +++ b/test/controller_test.dart @@ -5,11 +5,14 @@ import 'package:flutter_sticky_header/flutter_sticky_header.dart'; void main() { setUp(() { - WidgetsBinding.instance.renderView.configuration = TestViewConfiguration(size: Size(400, 800)); + WidgetsBinding.instance.renderView.configuration = + TestViewConfiguration(size: Size(400, 800)); }); - testWidgets('StickyHeaderController.stickyHeaderScrollOffset', (WidgetTester tester) async { - final StickyHeaderController stickyHeaderController = StickyHeaderController(); + testWidgets('StickyHeaderController.stickyHeaderScrollOffset', + (WidgetTester tester) async { + final StickyHeaderController stickyHeaderController = + StickyHeaderController(); final ScrollController scrollController = ScrollController(); await tester.pumpWidget( @@ -97,8 +100,10 @@ void main() { expect(stickyHeaderController.stickyHeaderScrollOffset, equals(880)); }); - testWidgets('StickyHeaderController.stickyHeaderScrollOffset - reverse', (WidgetTester tester) async { - final StickyHeaderController stickyHeaderController = StickyHeaderController(); + testWidgets('StickyHeaderController.stickyHeaderScrollOffset - reverse', + (WidgetTester tester) async { + final StickyHeaderController stickyHeaderController = + StickyHeaderController(); final ScrollController scrollController = ScrollController(); await tester.pumpWidget( diff --git a/test/sticky_test.dart b/test/sticky_test.dart index 28f2eb2..f855154 100644 --- a/test/sticky_test.dart +++ b/test/sticky_test.dart @@ -5,7 +5,8 @@ import 'package:flutter_sticky_header/flutter_sticky_header.dart'; void main() { setUp(() { - WidgetsBinding.instance.renderView.configuration = TestViewConfiguration(size: Size(400, 800)); + WidgetsBinding.instance.renderView.configuration = + TestViewConfiguration(size: Size(400, 800)); }); testWidgets('Mix sticky and not sticky headers', (WidgetTester tester) async { @@ -86,7 +87,8 @@ void main() { expect(header02Finder, findsNothing); }); - testWidgets('Mix sticky and not sticky headers - reverse', (WidgetTester tester) async { + testWidgets('Mix sticky and not sticky headers - reverse', + (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: Scaffold( @@ -161,7 +163,8 @@ void main() { expect(header00Finder, findsNothing); // Header #1 is in the tree (because the sliver is onstage). - expect(tester.getRect(header01Finder), const Rect.fromLTRB(0, 800, 400, 880)); + expect( + tester.getRect(header01Finder), const Rect.fromLTRB(0, 800, 400, 880)); expect(header02Finder, findsNothing); }); } From fb4c23f6d62dd45dcd73e926848b4ff0568eb810 Mon Sep 17 00:00:00 2001 From: Romain Rastel Date: Tue, 21 Jul 2020 23:51:54 +0200 Subject: [PATCH 12/30] update version --- CHANGELOG.md | 4 ++++ README.md | 2 +- pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82ac559..9197ad6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.4 +### Fixed +* Static analysis issues. + ## 0.4.3 ### Fixed * Static analysis issues. diff --git a/README.md b/README.md index e57876b..5790dda 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ In the `pubspec.yaml` of your flutter project, add the following dependency: ```yaml dependencies: ... - flutter_sticky_header: "^0.4.3" + flutter_sticky_header: "^0.4.4" ``` In your library add the following import: diff --git a/pubspec.yaml b/pubspec.yaml index a9c0cf1..29db5fb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_sticky_header description: Flutter implementation of sticky headers as a sliver. Use it in a CustomScrollView. -version: 0.4.3 +version: 0.4.4 homepage: https://github.com/letsar/flutter_sticky_header dependencies: From cd7dd40b4ab057dc84f8b196350669a71776d14e Mon Sep 17 00:00:00 2001 From: Romain Rastel Date: Sat, 25 Jul 2020 08:09:08 +0200 Subject: [PATCH 13/30] Fixes null references issues on debug --- CHANGELOG.md | 4 ++++ README.md | 2 +- lib/src/widgets/sliver_sticky_header.dart | 2 +- lib/src/widgets/sticky_header_layout_builder.dart | 2 +- pubspec.yaml | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9197ad6..25200bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.5 +### Fixed +* Null references issues in debug mode. + ## 0.4.4 ### Fixed * Static analysis issues. diff --git a/README.md b/README.md index 5790dda..7a78928 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ In the `pubspec.yaml` of your flutter project, add the following dependency: ```yaml dependencies: ... - flutter_sticky_header: "^0.4.4" + flutter_sticky_header: "^0.4.5" ``` In your library add the following import: diff --git a/lib/src/widgets/sliver_sticky_header.dart b/lib/src/widgets/sliver_sticky_header.dart index aaff895..4a56b34 100644 --- a/lib/src/widgets/sliver_sticky_header.dart +++ b/lib/src/widgets/sliver_sticky_header.dart @@ -285,9 +285,9 @@ class SliverStickyHeaderRenderObjectElement extends RenderObjectElement { @override void forgetChild(Element child) { + super.forgetChild(child); if (child == _header) _header = null; if (child == _sliver) _sliver = null; - super.forgetChild(child); } @override diff --git a/lib/src/widgets/sticky_header_layout_builder.dart b/lib/src/widgets/sticky_header_layout_builder.dart index 2de29ab..f0cfa0e 100644 --- a/lib/src/widgets/sticky_header_layout_builder.dart +++ b/lib/src/widgets/sticky_header_layout_builder.dart @@ -55,8 +55,8 @@ class _StickyHeaderLayoutBuilderElement extends RenderObjectElement { @override void forgetChild(Element child) { assert(child == _child); - _child = null; super.forgetChild(child); + _child = null; } @override diff --git a/pubspec.yaml b/pubspec.yaml index 29db5fb..3722e51 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_sticky_header description: Flutter implementation of sticky headers as a sliver. Use it in a CustomScrollView. -version: 0.4.4 +version: 0.4.5 homepage: https://github.com/letsar/flutter_sticky_header dependencies: From 20456eb80cfafd0dc27dc4f68f6f243f0a9ca087 Mon Sep 17 00:00:00 2001 From: Romain Rastel Date: Sat, 29 Aug 2020 22:22:53 +0200 Subject: [PATCH 14/30] Add a dependency to value_layout_builder --- CHANGELOG.md | 8 + README.md | 12 +- example/.gitignore | 7 + example/.metadata | 2 +- example/android/.gitignore | 4 + example/android/app/build.gradle | 6 +- .../android/app/src/debug/AndroidManifest.xml | 2 +- .../android/app/src/main/AndroidManifest.xml | 19 ++- .../com/letsar/fsh/example/MainActivity.kt | 12 -- .../com/letsar/sh/example/MainActivity.kt | 6 + .../app/src/main/res/values/styles.xml | 10 ++ .../app/src/profile/AndroidManifest.xml | 2 +- example/android/settings.gradle | 18 +-- example/ios/Runner.xcodeproj/project.pbxproj | 35 +--- .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + example/ios/Runner/Runner-Bridging-Header.h | 2 +- example/lib/examples/animated_header.dart | 2 +- example/pubspec.yaml | 18 ++- lib/src/rendering/sliver_sticky_header.dart | 21 +-- .../rendering/sticky_header_constraints.dart | 45 ------ .../sticky_header_layout_builder.dart | 81 ---------- lib/src/widgets/sliver_sticky_header.dart | 46 +++++- .../widgets/sticky_header_layout_builder.dart | 152 ------------------ pubspec.yaml | 5 +- 27 files changed, 174 insertions(+), 373 deletions(-) delete mode 100644 example/android/app/src/main/kotlin/com/letsar/fsh/example/MainActivity.kt create mode 100644 example/android/app/src/main/kotlin/com/letsar/sh/example/MainActivity.kt create mode 100644 example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 lib/src/rendering/sticky_header_constraints.dart delete mode 100644 lib/src/rendering/sticky_header_layout_builder.dart delete mode 100644 lib/src/widgets/sticky_header_layout_builder.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 25200bd..4268022 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.4.6 +### Added +* A new SliverStickyHeader.builder constructor instead of the deprecated SliverStickyHeaderBuilder. +* A dependency to value_layout_builder in order to manage the SliverStickyHeader.builder. + +### Removed +* Custom code to make SliverStickyHeader.builder work. + ## 0.4.5 ### Fixed * Null references issues in debug mode. diff --git a/README.md b/README.md index 7a78928..240a9f2 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ In the `pubspec.yaml` of your flutter project, add the following dependency: ```yaml dependencies: ... - flutter_sticky_header: "^0.4.5" + flutter_sticky_header: ``` In your library add the following import: @@ -65,14 +65,14 @@ SliverStickyHeader( ); ``` -## SliverStickyHeaderBuilder +## SliverStickyHeader.builder -If you want to change the header layout during its scroll, you can use the `SliverStickyHeaderBuilder`. +If you want to change the header layout during its scroll, you can use the `SliverStickyHeader.builder` constructor. The example belows changes the opacity of the header as it scrolls off the viewport. ```dart -SliverStickyHeaderBuilder( +SliverStickyHeader.builder( builder: (context, state) => Container( height: 60.0, color: (state.isPinned ? Colors.pink : Colors.lightBlue) @@ -100,6 +100,10 @@ SliverStickyHeaderBuilder( You can find more examples in the [Example](https://github.com/letsar/flutter_sticky_header/tree/master/example) project. +## Sponsoring + +I'm working on my packages on my free-time, but I don't have as much time as I would. If this package or any other package I created is helping you, please consider to sponsor me. By doing so, I will prioritize your issues or your pull-requests before the others. + ## Changelog Please see the [Changelog](https://github.com/letsar/flutter_sticky_header/blob/master/CHANGELOG.md) page to know what's recently changed. diff --git a/example/.gitignore b/example/.gitignore index ae1f183..f3c2053 100644 --- a/example/.gitignore +++ b/example/.gitignore @@ -22,6 +22,7 @@ # Flutter/Dart/Pub related **/doc/api/ +**/ios/Flutter/.last_build_id .dart_tool/ .flutter-plugins .flutter-plugins-dependencies @@ -33,5 +34,11 @@ # Web related lib/generated_plugin_registrant.dart +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + # Exceptions to above rules. !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/example/.metadata b/example/.metadata index 5d1241e..bcef94e 100644 --- a/example/.metadata +++ b/example/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled and should not be manually edited. version: - revision: 9f5ff2306bb3e30b2b98eee79cd231b1336f41f4 + revision: bbfbf1770cca2da7c82e887e4e4af910034800b6 channel: stable project_type: app diff --git a/example/android/.gitignore b/example/android/.gitignore index bc2100d..0a741cb 100644 --- a/example/android/.gitignore +++ b/example/android/.gitignore @@ -5,3 +5,7 @@ gradle-wrapper.jar /gradlew.bat /local.properties GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index bee1849..32feb18 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -38,12 +38,11 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.letsar.fsh.example" + applicationId "com.letsar.sh.example" minSdkVersion 16 targetSdkVersion 28 versionCode flutterVersionCode.toInteger() versionName flutterVersionName - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { @@ -61,7 +60,4 @@ flutter { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' } diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml index 91b46c0..6ea83aa 100644 --- a/example/android/app/src/debug/AndroidManifest.xml +++ b/example/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="com.letsar.sh.example"> diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 1a441e9..d401f8e 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="com.letsar.sh.example"> + + + diff --git a/example/android/app/src/main/kotlin/com/letsar/fsh/example/MainActivity.kt b/example/android/app/src/main/kotlin/com/letsar/fsh/example/MainActivity.kt deleted file mode 100644 index 09a0f4c..0000000 --- a/example/android/app/src/main/kotlin/com/letsar/fsh/example/MainActivity.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.letsar.fsh.example - -import androidx.annotation.NonNull; -import io.flutter.embedding.android.FlutterActivity -import io.flutter.embedding.engine.FlutterEngine -import io.flutter.plugins.GeneratedPluginRegistrant - -class MainActivity: FlutterActivity() { - override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { - GeneratedPluginRegistrant.registerWith(flutterEngine); - } -} diff --git a/example/android/app/src/main/kotlin/com/letsar/sh/example/MainActivity.kt b/example/android/app/src/main/kotlin/com/letsar/sh/example/MainActivity.kt new file mode 100644 index 0000000..e4b5f8e --- /dev/null +++ b/example/android/app/src/main/kotlin/com/letsar/sh/example/MainActivity.kt @@ -0,0 +1,6 @@ +package com.letsar.sh.example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml index 00fa441..1f83a33 100644 --- a/example/android/app/src/main/res/values/styles.xml +++ b/example/android/app/src/main/res/values/styles.xml @@ -1,8 +1,18 @@ + + + diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml index 91b46c0..6ea83aa 100644 --- a/example/android/app/src/profile/AndroidManifest.xml +++ b/example/android/app/src/profile/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="com.letsar.sh.example"> diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 5a2f14f..44e62bc 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,15 +1,11 @@ include ':app' -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } -} +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 85e59b4..d7a4911 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,11 +9,7 @@ /* 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, ); }; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 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, ); }; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -26,8 +22,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -38,13 +32,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 = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; 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; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -57,8 +49,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -68,9 +58,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -102,7 +90,6 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, @@ -111,13 +98,6 @@ path = Runner; sourceTree = ""; }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { - isa = PBXGroup; - children = ( - ); - name = "Supporting Files"; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -148,7 +128,7 @@ isa = PBXProject; attributes = { LastUpgradeCheck = 1020; - ORGANIZATIONNAME = "The Chromium Authors"; + ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; @@ -157,7 +137,7 @@ }; }; buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 3.2"; + compatibilityVersion = "Xcode 9.3"; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -201,7 +181,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; @@ -253,7 +233,6 @@ /* Begin XCBuildConfiguration section */ 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -320,7 +299,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.letsar.fsh.example; + PRODUCT_BUNDLE_IDENTIFIER = com.letsar.sh.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -330,7 +309,6 @@ }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -386,7 +364,6 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; @@ -454,7 +431,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.letsar.fsh.example; + PRODUCT_BUNDLE_IDENTIFIER = com.letsar.sh.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -481,7 +458,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.letsar.fsh.example; + PRODUCT_BUNDLE_IDENTIFIER = com.letsar.sh.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h index 7335fdf..308a2a5 100644 --- a/example/ios/Runner/Runner-Bridging-Header.h +++ b/example/ios/Runner/Runner-Bridging-Header.h @@ -1 +1 @@ -#import "GeneratedPluginRegistrant.h" \ No newline at end of file +#import "GeneratedPluginRegistrant.h" diff --git a/example/lib/examples/animated_header.dart b/example/lib/examples/animated_header.dart index 882b07a..01237e8 100644 --- a/example/lib/examples/animated_header.dart +++ b/example/lib/examples/animated_header.dart @@ -32,7 +32,7 @@ class _StickyHeaderList extends StatelessWidget { @override Widget build(BuildContext context) { - return SliverStickyHeaderBuilder( + return SliverStickyHeader.builder( builder: (context, state) => _AnimatedHeader( state: state, index: index, diff --git a/example/pubspec.yaml b/example/pubspec.yaml index ade1327..04c5dc2 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,6 +1,10 @@ name: example description: A new Flutter project. +# The following line prevents the package from being accidentally published to +# pub.dev using `pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 # followed by an optional build number separated by a +. @@ -19,20 +23,18 @@ environment: dependencies: flutter: sdk: flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.2 - flutter_sticky_header: path: ../ + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^0.1.3 + dev_dependencies: flutter_test: sdk: flutter - # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -46,8 +48,8 @@ flutter: # To add assets to your application, add an assets section, like this: # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index 329dc71..83b7262 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -4,8 +4,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart'; -import 'package:flutter_sticky_header/src/rendering/sticky_header_constraints.dart'; -import 'package:flutter_sticky_header/src/rendering/sticky_header_layout_builder.dart'; +import 'package:value_layout_builder/value_layout_builder.dart'; /// A sliver with a [RenderBox] as header and a [RenderSliver] as child. /// @@ -150,9 +149,9 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { if (header != null) { header.layout( - StickyHeaderConstraints( - state: _oldState ?? SliverStickyHeaderState(0.0, false), - boxConstraints: constraints.asBoxConstraints(), + BoxValueConstraints( + value: _oldState ?? SliverStickyHeaderState(0.0, false), + constraints: constraints.asBoxConstraints(), ), parentUsesSize: true, ); @@ -262,8 +261,10 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { controller?.stickyHeaderScrollOffset = constraints.precedingScrollExtent; } - // second layout if scroll percentage changed and header is a RenderStickyHeaderLayoutBuilder. - if (header is RenderStickyHeaderLayoutBuilder) { + // second layout if scroll percentage changed and header is a + // RenderStickyHeaderLayoutBuilder. + if (header is RenderConstrainedLayoutBuilder< + BoxValueConstraints, RenderBox>) { double headerScrollRatioClamped = headerScrollRatio.clamp(0.0, 1.0); SliverStickyHeaderState state = @@ -271,9 +272,9 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { if (_oldState != state) { _oldState = state; header.layout( - StickyHeaderConstraints( - state: _oldState, - boxConstraints: constraints.asBoxConstraints(), + BoxValueConstraints( + value: _oldState, + constraints: constraints.asBoxConstraints(), ), parentUsesSize: true, ); diff --git a/lib/src/rendering/sticky_header_constraints.dart b/lib/src/rendering/sticky_header_constraints.dart deleted file mode 100644 index 3119a73..0000000 --- a/lib/src/rendering/sticky_header_constraints.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/rendering.dart'; -import 'package:flutter_sticky_header/src/widgets/sliver_sticky_header.dart'; - -/// Immutable layout constraints for sticky header -class StickyHeaderConstraints extends BoxConstraints { - StickyHeaderConstraints({ - this.state, - BoxConstraints boxConstraints, - }) : assert(state != null), - assert(boxConstraints != null), - super( - minWidth: boxConstraints.minWidth, - maxWidth: boxConstraints.maxWidth, - minHeight: boxConstraints.minHeight, - maxHeight: boxConstraints.maxHeight, - ); - - final SliverStickyHeaderState state; - - @override - bool get isNormalized => - state.scrollPercentage >= 0.0 && - state.scrollPercentage <= 1.0 && - super.isNormalized; - - @override - bool operator ==(dynamic other) { - assert(debugAssertIsValid()); - if (identical(this, other)) return true; - if (other is! StickyHeaderConstraints) return false; - final StickyHeaderConstraints typedOther = other; - assert(typedOther.debugAssertIsValid()); - return state == typedOther.state && - minWidth == typedOther.minWidth && - maxWidth == typedOther.maxWidth && - minHeight == typedOther.minHeight && - maxHeight == typedOther.maxHeight; - } - - @override - int get hashCode { - assert(debugAssertIsValid()); - return hashValues(minWidth, maxWidth, minHeight, maxHeight, state); - } -} diff --git a/lib/src/rendering/sticky_header_layout_builder.dart b/lib/src/rendering/sticky_header_layout_builder.dart deleted file mode 100644 index 2d5cc44..0000000 --- a/lib/src/rendering/sticky_header_layout_builder.dart +++ /dev/null @@ -1,81 +0,0 @@ -import 'package:flutter/rendering.dart'; -import 'package:flutter/widgets.dart'; -import 'package:flutter_sticky_header/src/rendering/sticky_header_constraints.dart'; - -class RenderStickyHeaderLayoutBuilder extends RenderBox - with RenderObjectWithChildMixin { - RenderStickyHeaderLayoutBuilder({ - LayoutCallback callback, - }) : _callback = callback; - - LayoutCallback get callback => _callback; - LayoutCallback _callback; - set callback(LayoutCallback value) { - if (value == _callback) return; - _callback = value; - markNeedsLayout(); - } - - // layout input - @override - StickyHeaderConstraints get constraints => super.constraints; - - bool _debugThrowIfNotCheckingIntrinsics() { - assert(() { - if (!RenderObject.debugCheckingIntrinsics) { - throw FlutterError( - 'StickyHeaderLayoutBuilder does not support returning intrinsic dimensions.\n' - 'Calculating the intrinsic dimensions would require running the layout ' - 'callback speculatively, which might mutate the live render object tree.'); - } - return true; - }()); - return true; - } - - @override - double computeMinIntrinsicWidth(double height) { - assert(_debugThrowIfNotCheckingIntrinsics()); - return 0.0; - } - - @override - double computeMaxIntrinsicWidth(double height) { - assert(_debugThrowIfNotCheckingIntrinsics()); - return 0.0; - } - - @override - double computeMinIntrinsicHeight(double width) { - assert(_debugThrowIfNotCheckingIntrinsics()); - return 0.0; - } - - @override - double computeMaxIntrinsicHeight(double width) { - assert(_debugThrowIfNotCheckingIntrinsics()); - return 0.0; - } - - @override - void performLayout() { - assert(callback != null); - invokeLayoutCallback(callback); - if (child != null) { - child.layout(constraints, parentUsesSize: true); - size = constraints.constrain(child.size); - } else { - size = constraints.biggest; - } - } - - @override - bool hitTestChildren(HitTestResult result, {Offset position}) { - return child?.hitTest(result, position: position) ?? false; - } - - @override - void paint(PaintingContext context, Offset offset) { - if (child != null) context.paintChild(child, offset); - } -} diff --git a/lib/src/widgets/sliver_sticky_header.dart b/lib/src/widgets/sliver_sticky_header.dart index 4a56b34..df2e05c 100644 --- a/lib/src/widgets/sliver_sticky_header.dart +++ b/lib/src/widgets/sliver_sticky_header.dart @@ -1,8 +1,8 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_sticky_header/src/rendering/sliver_sticky_header.dart'; -import 'package:flutter_sticky_header/src/widgets/sticky_header_layout_builder.dart'; +import 'package:value_layout_builder/value_layout_builder.dart'; -/// Signature used by [SliverStickyHeaderBuilder] to build the header +/// Signature used by [SliverStickyHeader.builder] to build the header /// when the sticky header state has changed. typedef Widget SliverStickyHeaderWidgetBuilder( BuildContext context, @@ -140,14 +140,15 @@ class SliverStickyHeaderState { /// /// Place this widget inside a [CustomScrollView] or similar. class SliverStickyHeader extends RenderObjectWidget { - /// Creates a sliver that displays the [header] before its [sliver], unless [overlapsContent] it's true. + /// Creates a sliver that displays the [header] before its [sliver], unless + /// [overlapsContent] it's true. /// The [header] stays pinned when it hits the start of the viewport until /// the [sliver] scrolls off the viewport. /// /// The [overlapsContent] and [sticky] arguments must not be null. /// - /// If a [StickyHeaderController] is not provided, then the value of [DefaultStickyHeaderController.of] - /// will be used. + /// If a [StickyHeaderController] is not provided, then the value of + /// [DefaultStickyHeaderController.of] will be used. SliverStickyHeader({ Key key, this.header, @@ -159,6 +160,32 @@ class SliverStickyHeader extends RenderObjectWidget { assert(sticky != null), super(key: key); + /// Creates a widget that builds the header of a [SliverStickyHeader] + /// each time its scroll percentage changes. + /// + /// The [builder], [overlapsContent] and [sticky] arguments must not be null. + /// + /// If a [StickyHeaderController] is not provided, then the value of + /// [DefaultStickyHeaderController.of] will be used. + SliverStickyHeader.builder({ + Key key, + SliverStickyHeaderWidgetBuilder builder, + Widget sliver, + bool overlapsContent: false, + bool sticky = true, + StickyHeaderController controller, + }) : this( + key: key, + header: ValueLayoutBuilder( + builder: (context, constraints) => + builder(context, constraints.value), + ), + sliver: sliver, + overlapsContent: overlapsContent, + sticky: sticky, + controller: controller, + ); + /// The header to display before the sliver. final Widget header; @@ -194,7 +221,9 @@ class SliverStickyHeader extends RenderObjectWidget { @override void updateRenderObject( - BuildContext context, RenderSliverStickyHeader renderObject) { + BuildContext context, + RenderSliverStickyHeader renderObject, + ) { renderObject ..overlapsContent = overlapsContent ..sticky = sticky @@ -208,6 +237,7 @@ class SliverStickyHeader extends RenderObjectWidget { /// This is useful if you want to change the header layout when it starts to scroll off the viewport. /// You cannot change the main axis extent of the header in this builder otherwise it could result /// in strange behavior. +@Deprecated('Use SliverStickyHeader.builder instead.') class SliverStickyHeaderBuilder extends StatelessWidget { /// Creates a widget that builds the header of a [SliverStickyHeader] /// each time its scroll percentage changes. @@ -258,8 +288,8 @@ class SliverStickyHeaderBuilder extends StatelessWidget { sliver: sliver, sticky: sticky, controller: controller, - header: StickyHeaderLayoutBuilder( - builder: (context, constraints) => builder(context, constraints.state), + header: ValueLayoutBuilder( + builder: (context, constraints) => builder(context, constraints.value), ), ); } diff --git a/lib/src/widgets/sticky_header_layout_builder.dart b/lib/src/widgets/sticky_header_layout_builder.dart deleted file mode 100644 index f0cfa0e..0000000 --- a/lib/src/widgets/sticky_header_layout_builder.dart +++ /dev/null @@ -1,152 +0,0 @@ -import 'package:flutter/rendering.dart'; -import 'package:flutter/widgets.dart'; -import 'package:flutter_sticky_header/src/rendering/sticky_header_constraints.dart'; -import 'package:flutter_sticky_header/src/rendering/sticky_header_layout_builder.dart'; - -typedef Widget StickyHeaderLayoutWidgetBuilder( - BuildContext context, StickyHeaderConstraints constraints); - -/// Builds a widget tree that can depend on the [StickyHeaderConstraints]. -/// -/// This is used by [SliverStickyHeaderBuilder] to change the header layout -/// while it starts to scroll off the viewport. -class StickyHeaderLayoutBuilder extends RenderObjectWidget { - /// Creates a widget that defers its building until layout. - /// - /// The [builder] argument must not be null. - const StickyHeaderLayoutBuilder({ - Key key, - @required this.builder, - }) : assert(builder != null), - super(key: key); - - /// Called at layout time to construct the widget tree. The builder must not - /// return null. - final StickyHeaderLayoutWidgetBuilder builder; - - @override - RenderObjectElement createElement() => - _StickyHeaderLayoutBuilderElement(this); - - @override - RenderObject createRenderObject(BuildContext context) => - RenderStickyHeaderLayoutBuilder(); - - // updateRenderObject is redundant with the logic in the _StickyHeaderLayoutBuilderElement below. -} - -class _StickyHeaderLayoutBuilderElement extends RenderObjectElement { - _StickyHeaderLayoutBuilderElement(StickyHeaderLayoutBuilder widget) - : super(widget); - - @override - StickyHeaderLayoutBuilder get widget => super.widget; - - @override - RenderStickyHeaderLayoutBuilder get renderObject => super.renderObject; - - Element _child; - - @override - void visitChildren(ElementVisitor visitor) { - if (_child != null) visitor(_child); - } - - @override - void forgetChild(Element child) { - assert(child == _child); - super.forgetChild(child); - _child = null; - } - - @override - void mount(Element parent, dynamic newSlot) { - super.mount(parent, newSlot); // Creates the renderObject. - renderObject.callback = _layout; - } - - @override - void update(StickyHeaderLayoutBuilder newWidget) { - assert(widget != newWidget); - super.update(newWidget); - assert(widget == newWidget); - renderObject.callback = _layout; - renderObject.markNeedsLayout(); - } - - @override - void performRebuild() { - // This gets called if markNeedsBuild() is called on us. - // That might happen if, e.g., our builder uses Inherited widgets. - renderObject.markNeedsLayout(); - super - .performRebuild(); // Calls widget.updateRenderObject (a no-op in this case). - } - - @override - void unmount() { - renderObject.callback = null; - super.unmount(); - } - - void _layout(StickyHeaderConstraints constraints) { - owner.buildScope(this, () { - Widget built; - if (widget.builder != null) { - try { - built = widget.builder(this, constraints); - debugWidgetBuilderValue(widget, built); - } catch (e, stack) { - built = ErrorWidget.builder( - _debugReportException('building $widget', e, stack)); - } - } - try { - _child = updateChild(_child, built, null); - assert(_child != null); - } catch (e, stack) { - built = ErrorWidget.builder( - _debugReportException('building $widget', e, stack)); - _child = updateChild(null, built, slot); - } - }); - } - - @override - void insertChildRenderObject(RenderObject child, slot) { - final RenderObjectWithChildMixin renderObject = - this.renderObject; - assert(slot == null); - assert(renderObject.debugValidateChild(child)); - renderObject.child = child; - assert(renderObject == this.renderObject); - } - - @override - void moveChildRenderObject(RenderObject child, slot) { - assert(false); - } - - @override - void removeChildRenderObject(RenderObject child) { - final RenderStickyHeaderLayoutBuilder renderObject = this.renderObject; - assert(renderObject.child == child); - renderObject.child = null; - assert(renderObject == this.renderObject); - } -} - -FlutterErrorDetails _debugReportException( - String context, - dynamic exception, - StackTrace stack, -) { - final FlutterErrorDetails details = FlutterErrorDetails( - exception: exception, - stack: stack, - library: 'flutter_sticky_header widgets library', - context: ErrorDescription(context), - ); - FlutterError.reportError(details); - return details; -} diff --git a/pubspec.yaml b/pubspec.yaml index 3722e51..3a2e0ce 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,11 +1,12 @@ name: flutter_sticky_header description: Flutter implementation of sticky headers as a sliver. Use it in a CustomScrollView. -version: 0.4.5 +version: 0.4.6 homepage: https://github.com/letsar/flutter_sticky_header dependencies: flutter: sdk: flutter + value_layout_builder: ^0.1.0 dev_dependencies: flutter_test: @@ -13,4 +14,4 @@ dev_dependencies: environment: sdk: ">=2.2.0 <3.0.0" - flutter: ">=1.12.0 <2.0.0" + flutter: ">=1.17.0 <2.0.0" From b2f00dfa03cded647cc24916c3c09ee74da88f26 Mon Sep 17 00:00:00 2001 From: Romain Rastel Date: Tue, 1 Sep 2020 13:29:29 +0200 Subject: [PATCH 15/30] changed the minimum version of Flutter --- CHANGELOG.md | 4 ++++ pubspec.yaml | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4268022..054bf2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.0 +### Changed +* The minimum version of Flutter. + ## 0.4.6 ### Added * A new SliverStickyHeader.builder constructor instead of the deprecated SliverStickyHeaderBuilder. diff --git a/pubspec.yaml b/pubspec.yaml index 3a2e0ce..a5e665d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,12 +1,12 @@ name: flutter_sticky_header description: Flutter implementation of sticky headers as a sliver. Use it in a CustomScrollView. -version: 0.4.6 +version: 0.5.0 homepage: https://github.com/letsar/flutter_sticky_header dependencies: flutter: sdk: flutter - value_layout_builder: ^0.1.0 + value_layout_builder: ^0.2.0 dev_dependencies: flutter_test: @@ -14,4 +14,4 @@ dev_dependencies: environment: sdk: ">=2.2.0 <3.0.0" - flutter: ">=1.17.0 <2.0.0" + flutter: ">=1.20.0 <2.0.0" From 602d501eda46156b652dd6a4ade7b21e1eb58190 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Mon, 29 Mar 2021 11:03:41 +0200 Subject: [PATCH 16/30] Migrate to null-safety --- lib/src/rendering/sliver_sticky_header.dart | 171 ++++++++++---------- lib/src/widgets/sliver_sticky_header.dart | 86 +++++----- pubspec.yaml | 7 +- test/controller_test.dart | 10 +- test/sticky_test.dart | 10 +- 5 files changed, 142 insertions(+), 142 deletions(-) diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index 83b7262..bc0a513 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -12,28 +12,26 @@ import 'package:value_layout_builder/value_layout_builder.dart'; /// the [child] scrolls off the viewport. class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { RenderSliverStickyHeader({ - RenderObject header, - RenderSliver child, + RenderObject? header, + RenderSliver? child, bool overlapsContent: false, bool sticky: true, - StickyHeaderController controller, - }) : assert(overlapsContent != null), - assert(sticky != null), - _overlapsContent = overlapsContent, + StickyHeaderController? controller, + }) : _overlapsContent = overlapsContent, _sticky = sticky, _controller = controller { - this.header = header; + this.header = header as RenderBox?; this.child = child; } - SliverStickyHeaderState _oldState; - double _headerExtent; - bool _isPinned; + SliverStickyHeaderState? _oldState; + double? _headerExtent; + late bool _isPinned; bool get overlapsContent => _overlapsContent; bool _overlapsContent; + set overlapsContent(bool value) { - assert(value != null); if (_overlapsContent == value) return; _overlapsContent = value; markNeedsLayout(); @@ -41,40 +39,43 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { bool get sticky => _sticky; bool _sticky; + set sticky(bool value) { - assert(value != null); if (_sticky == value) return; _sticky = value; markNeedsLayout(); } - StickyHeaderController get controller => _controller; - StickyHeaderController _controller; - set controller(StickyHeaderController value) { + StickyHeaderController? get controller => _controller; + StickyHeaderController? _controller; + + set controller(StickyHeaderController? value) { if (_controller == value) return; if (_controller != null && value != null) { // We copy the state of the old controller. - value.stickyHeaderScrollOffset = _controller.stickyHeaderScrollOffset; + value.stickyHeaderScrollOffset = _controller!.stickyHeaderScrollOffset; } _controller = value; } /// The render object's header - RenderBox get header => _header; - RenderBox _header; - set header(RenderBox value) { - if (_header != null) dropChild(_header); + RenderBox? get header => _header; + RenderBox? _header; + + set header(RenderBox? value) { + if (_header != null) dropChild(_header!); _header = value; - if (_header != null) adoptChild(_header); + if (_header != null) adoptChild(_header!); } /// The render object's unique child - RenderSliver get child => _child; - RenderSliver _child; - set child(RenderSliver value) { - if (_child != null) dropChild(_child); + RenderSliver? get child => _child; + RenderSliver? _child; + + set child(RenderSliver? value) { + if (_child != null) dropChild(_child!); _child = value; - if (_child != null) adoptChild(_child); + if (_child != null) adoptChild(_child!); } @override @@ -86,55 +87,53 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { @override void attach(PipelineOwner owner) { super.attach(owner); - if (_header != null) _header.attach(owner); - if (_child != null) _child.attach(owner); + if (_header != null) _header!.attach(owner); + if (_child != null) _child!.attach(owner); } @override void detach() { super.detach(); - if (_header != null) _header.detach(); - if (_child != null) _child.detach(); + if (_header != null) _header!.detach(); + if (_child != null) _child!.detach(); } @override void redepthChildren() { - if (_header != null) redepthChild(_header); - if (_child != null) redepthChild(_child); + if (_header != null) redepthChild(_header!); + if (_child != null) redepthChild(_child!); } @override void visitChildren(RenderObjectVisitor visitor) { - if (_header != null) visitor(_header); - if (_child != null) visitor(_child); + if (_header != null) visitor(_header!); + if (_child != null) visitor(_child!); } @override List debugDescribeChildren() { List result = []; if (header != null) { - result.add(header.toDiagnosticsNode(name: 'header')); + result.add(header!.toDiagnosticsNode(name: 'header')); } if (child != null) { - result.add(child.toDiagnosticsNode(name: 'child')); + result.add(child!.toDiagnosticsNode(name: 'child')); } return result; } - double computeHeaderExtent() { + double? computeHeaderExtent() { if (header == null) return 0.0; - assert(header.hasSize); - assert(constraints.axis != null); + assert(header!.hasSize); switch (constraints.axis) { case Axis.vertical: - return header.size.height; + return header!.size.height; case Axis.horizontal: - return header.size.width; + return header!.size.width; } - return null; } - double get headerLogicalExtent => overlapsContent ? 0.0 : _headerExtent; + double? get headerLogicalExtent => overlapsContent ? 0.0 : _headerExtent; @override void performLayout() { @@ -148,7 +147,7 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { constraints.axisDirection, constraints.growthDirection); if (header != null) { - header.layout( + header!.layout( BoxValueConstraints( value: _oldState ?? SliverStickyHeaderState(0.0, false), constraints: constraints.asBoxConstraints(), @@ -159,7 +158,7 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { } // Compute the header extent only one time. - double headerExtent = headerLogicalExtent; + double headerExtent = headerLogicalExtent!; final double headerPaintExtent = calculatePaintOffset(constraints, from: 0.0, to: headerExtent); final double headerCacheExtent = @@ -175,7 +174,7 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { hasVisualOverflow: headerExtent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0); } else { - child.layout( + child!.layout( constraints.copyWith( scrollOffset: math.max(0.0, constraints.scrollOffset - headerExtent), cacheOrigin: math.min(0.0, constraints.cacheOrigin + headerExtent), @@ -187,7 +186,7 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { ), parentUsesSize: true, ); - final SliverGeometry childLayoutGeometry = child.geometry; + final SliverGeometry childLayoutGeometry = child!.geometry!; if (childLayoutGeometry.scrollOffsetCorrection != null) { geometry = SliverGeometry( scrollOffsetCorrection: childLayoutGeometry.scrollOffsetCorrection, @@ -217,37 +216,37 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { hasVisualOverflow: childLayoutGeometry.hasVisualOverflow, ); - final SliverPhysicalParentData childParentData = child.parentData; - assert(constraints.axisDirection != null); - assert(constraints.growthDirection != null); + final SliverPhysicalParentData? childParentData = + child!.parentData as SliverPhysicalParentData?; switch (axisDirection) { case AxisDirection.up: - childParentData.paintOffset = Offset.zero; + childParentData!.paintOffset = Offset.zero; break; case AxisDirection.right: - childParentData.paintOffset = Offset( + childParentData!.paintOffset = Offset( calculatePaintOffset(constraints, from: 0.0, to: headerExtent), 0.0); break; case AxisDirection.down: - childParentData.paintOffset = Offset(0.0, + childParentData!.paintOffset = Offset(0.0, calculatePaintOffset(constraints, from: 0.0, to: headerExtent)); break; case AxisDirection.left: - childParentData.paintOffset = Offset.zero; + childParentData!.paintOffset = Offset.zero; break; } } if (header != null) { - final SliverPhysicalParentData headerParentData = header.parentData; + final SliverPhysicalParentData? headerParentData = + header!.parentData as SliverPhysicalParentData?; final double childScrollExtent = child?.geometry?.scrollExtent ?? 0.0; final double headerPosition = sticky ? math.min( constraints.overlap, childScrollExtent - constraints.scrollOffset - - (overlapsContent ? _headerExtent : 0.0)) + (overlapsContent ? _headerExtent! : 0.0)) : -constraints.scrollOffset; _isPinned = sticky && @@ -256,7 +255,7 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { constraints.viewportMainAxisExtent); final double headerScrollRatio = - ((headerPosition - constraints.overlap).abs() / _headerExtent); + ((headerPosition - constraints.overlap).abs() / _headerExtent!); if (_isPinned && headerScrollRatio <= 1) { controller?.stickyHeaderScrollOffset = constraints.precedingScrollExtent; @@ -271,8 +270,8 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { SliverStickyHeaderState(headerScrollRatioClamped, _isPinned); if (_oldState != state) { _oldState = state; - header.layout( - BoxValueConstraints( + header!.layout( + BoxValueConstraints( value: _oldState, constraints: constraints.asBoxConstraints(), ), @@ -283,18 +282,18 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { switch (axisDirection) { case AxisDirection.up: - headerParentData.paintOffset = Offset( - 0.0, geometry.paintExtent - headerPosition - _headerExtent); + headerParentData!.paintOffset = Offset( + 0.0, geometry!.paintExtent - headerPosition - _headerExtent!); break; case AxisDirection.down: - headerParentData.paintOffset = Offset(0.0, headerPosition); + headerParentData!.paintOffset = Offset(0.0, headerPosition); break; case AxisDirection.left: - headerParentData.paintOffset = Offset( - geometry.paintExtent - headerPosition - _headerExtent, 0.0); + headerParentData!.paintOffset = Offset( + geometry!.paintExtent - headerPosition - _headerExtent!, 0.0); break; case AxisDirection.right: - headerParentData.paintOffset = Offset(headerPosition, 0.0); + headerParentData!.paintOffset = Offset(headerPosition, 0.0); break; } } @@ -302,25 +301,25 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { @override bool hitTestChildren(SliverHitTestResult result, - {@required double mainAxisPosition, @required double crossAxisPosition}) { - assert(geometry.hitTestExtent > 0.0); + {required double mainAxisPosition, required double crossAxisPosition}) { + assert(geometry!.hitTestExtent > 0.0); if (header != null && - mainAxisPosition - constraints.overlap <= _headerExtent) { + mainAxisPosition - constraints.overlap <= _headerExtent!) { return hitTestBoxChild( BoxHitTestResult.wrap(SliverHitTestResult.wrap(result)), - header, + header!, mainAxisPosition: mainAxisPosition - constraints.overlap, crossAxisPosition: crossAxisPosition, ) || (_overlapsContent && child != null && - child.geometry.hitTestExtent > 0.0 && - child.hitTest(result, + child!.geometry!.hitTestExtent > 0.0 && + child!.hitTest(result, mainAxisPosition: mainAxisPosition - childMainAxisPosition(child), crossAxisPosition: crossAxisPosition)); - } else if (child != null && child.geometry.hitTestExtent > 0.0) { - return child.hitTest(result, + } else if (child != null && child!.geometry!.hitTestExtent > 0.0) { + return child!.hitTest(result, mainAxisPosition: mainAxisPosition - childMainAxisPosition(child), crossAxisPosition: crossAxisPosition); } @@ -328,19 +327,19 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { } @override - double childMainAxisPosition(RenderObject child) { + double childMainAxisPosition(RenderObject? child) { if (child == header) return _isPinned ? 0.0 : -(constraints.scrollOffset + constraints.overlap); if (child == this.child) return calculatePaintOffset(constraints, - from: 0.0, to: headerLogicalExtent); - return null; + from: 0.0, to: headerLogicalExtent!); + return 0; } @override - double childScrollOffset(RenderObject child) { + double? childScrollOffset(RenderObject child) { assert(child.parent == this); if (child == this.child) { return _headerExtent; @@ -351,23 +350,25 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { @override void applyPaintTransform(RenderObject child, Matrix4 transform) { - assert(child != null); - final SliverPhysicalParentData childParentData = child.parentData; + final SliverPhysicalParentData childParentData = + child.parentData as SliverPhysicalParentData; childParentData.applyPaintTransform(transform); } @override void paint(PaintingContext context, Offset offset) { - if (geometry.visible) { - if (child != null && child.geometry.visible) { - final SliverPhysicalParentData childParentData = child.parentData; - context.paintChild(child, offset + childParentData.paintOffset); + if (geometry!.visible) { + if (child != null && child!.geometry!.visible) { + final SliverPhysicalParentData childParentData = + child!.parentData as SliverPhysicalParentData; + context.paintChild(child!, offset + childParentData.paintOffset); } // The header must be drawn over the sliver. if (header != null) { - final SliverPhysicalParentData headerParentData = header.parentData; - context.paintChild(header, offset + headerParentData.paintOffset); + final SliverPhysicalParentData headerParentData = + header!.parentData as SliverPhysicalParentData; + context.paintChild(header!, offset + headerParentData.paintOffset); } } } diff --git a/lib/src/widgets/sliver_sticky_header.dart b/lib/src/widgets/sliver_sticky_header.dart index df2e05c..6a98f6c 100644 --- a/lib/src/widgets/sliver_sticky_header.dart +++ b/lib/src/widgets/sliver_sticky_header.dart @@ -1,3 +1,4 @@ +import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_sticky_header/src/rendering/sliver_sticky_header.dart'; import 'package:value_layout_builder/value_layout_builder.dart'; @@ -20,7 +21,6 @@ class StickyHeaderController with ChangeNotifier { /// This setter should only be used by flutter_sticky_header package. set stickyHeaderScrollOffset(double value) { - assert(value != null); if (_stickyHeaderScrollOffset != value) { _stickyHeaderScrollOffset = value; notifyListeners(); @@ -38,8 +38,8 @@ class StickyHeaderController with ChangeNotifier { /// widgets. class DefaultStickyHeaderController extends StatefulWidget { const DefaultStickyHeaderController({ - Key key, - @required this.child, + Key? key, + required this.child, }) : super(key: key); /// The widget below this widget in the tree. @@ -56,8 +56,8 @@ class DefaultStickyHeaderController extends StatefulWidget { /// ```dart /// StickyHeaderController controller = DefaultStickyHeaderController.of(context); /// ``` - static StickyHeaderController of(BuildContext context) { - final _StickyHeaderControllerScope scope = context + static StickyHeaderController? of(BuildContext context) { + final _StickyHeaderControllerScope? scope = context .dependOnInheritedWidgetOfExactType<_StickyHeaderControllerScope>(); return scope?.controller; } @@ -69,7 +69,7 @@ class DefaultStickyHeaderController extends StatefulWidget { class _DefaultStickyHeaderControllerState extends State { - StickyHeaderController _controller; + StickyHeaderController? _controller; @override void initState() { @@ -79,7 +79,7 @@ class _DefaultStickyHeaderControllerState @override void dispose() { - _controller.dispose(); + _controller!.dispose(); super.dispose(); } @@ -94,12 +94,12 @@ class _DefaultStickyHeaderControllerState class _StickyHeaderControllerScope extends InheritedWidget { const _StickyHeaderControllerScope({ - Key key, + Key? key, this.controller, - Widget child, + required Widget child, }) : super(key: key, child: child); - final StickyHeaderController controller; + final StickyHeaderController? controller; @override bool updateShouldNotify(_StickyHeaderControllerScope old) { @@ -113,8 +113,7 @@ class SliverStickyHeaderState { const SliverStickyHeaderState( this.scrollPercentage, this.isPinned, - ) : assert(scrollPercentage != null), - assert(isPinned != null); + ); final double scrollPercentage; @@ -150,15 +149,13 @@ class SliverStickyHeader extends RenderObjectWidget { /// If a [StickyHeaderController] is not provided, then the value of /// [DefaultStickyHeaderController.of] will be used. SliverStickyHeader({ - Key key, + Key? key, this.header, this.sliver, this.overlapsContent: false, this.sticky = true, this.controller, - }) : assert(overlapsContent != null), - assert(sticky != null), - super(key: key); + }) : super(key: key); /// Creates a widget that builds the header of a [SliverStickyHeader] /// each time its scroll percentage changes. @@ -168,17 +165,17 @@ class SliverStickyHeader extends RenderObjectWidget { /// If a [StickyHeaderController] is not provided, then the value of /// [DefaultStickyHeaderController.of] will be used. SliverStickyHeader.builder({ - Key key, - SliverStickyHeaderWidgetBuilder builder, - Widget sliver, + Key? key, + SliverStickyHeaderWidgetBuilder? builder, + Widget? sliver, bool overlapsContent: false, bool sticky = true, - StickyHeaderController controller, + StickyHeaderController? controller, }) : this( key: key, header: ValueLayoutBuilder( builder: (context, constraints) => - builder(context, constraints.value), + builder!(context, constraints.value), ), sliver: sliver, overlapsContent: overlapsContent, @@ -187,10 +184,10 @@ class SliverStickyHeader extends RenderObjectWidget { ); /// The header to display before the sliver. - final Widget header; + final Widget? header; /// The sliver to display after the header. - final Widget sliver; + final Widget? sliver; /// Whether the header should be drawn on top of the sliver /// instead of before. @@ -204,7 +201,7 @@ class SliverStickyHeader extends RenderObjectWidget { /// /// If a [StickyHeaderController] is not provided, then the value of [DefaultStickyHeaderController.of] /// will be used. - final StickyHeaderController controller; + final StickyHeaderController? controller; @override RenderSliverStickyHeader createRenderObject(BuildContext context) { @@ -247,16 +244,13 @@ class SliverStickyHeaderBuilder extends StatelessWidget { /// If a [StickyHeaderController] is not provided, then the value of [DefaultStickyHeaderController.of] /// will be used. const SliverStickyHeaderBuilder({ - Key key, - @required this.builder, + Key? key, + required this.builder, this.sliver, this.overlapsContent: false, this.sticky = true, this.controller, - }) : assert(builder != null), - assert(overlapsContent != null), - assert(sticky != null), - super(key: key); + }) : super(key: key); /// Called to build the [SliverStickyHeader]'s header. /// @@ -265,7 +259,7 @@ class SliverStickyHeaderBuilder extends StatelessWidget { final SliverStickyHeaderWidgetBuilder builder; /// The sliver to display after the header. - final Widget sliver; + final Widget? sliver; /// Whether the header should be drawn on top of the sliver /// instead of before. @@ -279,7 +273,7 @@ class SliverStickyHeaderBuilder extends StatelessWidget { /// /// If a [StickyHeaderController] is not provided, then the value of [DefaultStickyHeaderController.of] /// will be used. - final StickyHeaderController controller; + final StickyHeaderController? controller; @override Widget build(BuildContext context) { @@ -301,16 +295,16 @@ class SliverStickyHeaderRenderObjectElement extends RenderObjectElement { : super(widget); @override - SliverStickyHeader get widget => super.widget; + SliverStickyHeader get widget => super.widget as SliverStickyHeader; - Element _header; + Element? _header; - Element _sliver; + Element? _sliver; @override void visitChildren(ElementVisitor visitor) { - if (_header != null) visitor(_header); - if (_sliver != null) visitor(_sliver); + if (_header != null) visitor(_header!); + if (_sliver != null) visitor(_sliver!); } @override @@ -321,7 +315,7 @@ class SliverStickyHeaderRenderObjectElement extends RenderObjectElement { } @override - void mount(Element parent, dynamic newSlot) { + void mount(Element? parent, dynamic newSlot) { super.mount(parent, newSlot); _header = updateChild(_header, widget.header, 0); _sliver = updateChild(_sliver, widget.sliver, 1); @@ -336,21 +330,23 @@ class SliverStickyHeaderRenderObjectElement extends RenderObjectElement { } @override - void insertChildRenderObject(RenderObject child, int slot) { - final RenderSliverStickyHeader renderObject = this.renderObject; - if (slot == 0) renderObject.header = child; - if (slot == 1) renderObject.child = child; + void insertRenderObjectChild(RenderObject child, int? slot) { + final RenderSliverStickyHeader renderObject = + this.renderObject as RenderSliverStickyHeader; + if (slot == 0) renderObject.header = child as RenderBox?; + if (slot == 1) renderObject.child = child as RenderSliver?; assert(renderObject == this.renderObject); } @override - void moveChildRenderObject(RenderObject child, slot) { + void moveRenderObjectChild(RenderObject child, slot, newSlot) { assert(false); } @override - void removeChildRenderObject(RenderObject child) { - final RenderSliverStickyHeader renderObject = this.renderObject; + void removeRenderObjectChild(RenderObject child, slot) { + final RenderSliverStickyHeader renderObject = + this.renderObject as RenderSliverStickyHeader; if (renderObject.header == child) renderObject.header = null; if (renderObject.child == child) renderObject.child = null; assert(renderObject == this.renderObject); diff --git a/pubspec.yaml b/pubspec.yaml index a5e665d..7ab42c6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,12 +6,15 @@ homepage: https://github.com/letsar/flutter_sticky_header dependencies: flutter: sdk: flutter - value_layout_builder: ^0.2.0 + value_layout_builder: + git: + url: https://github.com/John-Dormevil/value_layout_builder.git + ref: null-safety dev_dependencies: flutter_test: sdk: flutter environment: - sdk: ">=2.2.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' flutter: ">=1.20.0 <2.0.0" diff --git a/test/controller_test.dart b/test/controller_test.dart index fbca4f0..827a979 100644 --- a/test/controller_test.dart +++ b/test/controller_test.dart @@ -5,7 +5,7 @@ import 'package:flutter_sticky_header/flutter_sticky_header.dart'; void main() { setUp(() { - WidgetsBinding.instance.renderView.configuration = + WidgetsBinding.instance!.renderView.configuration = TestViewConfiguration(size: Size(400, 800)); }); @@ -195,8 +195,8 @@ void main() { class _Header extends StatelessWidget { const _Header({ - Key key, - @required this.index, + Key? key, + required this.index, }) : super(key: key); final int index; @@ -213,7 +213,7 @@ class _Header extends StatelessWidget { class _Sliver extends StatelessWidget { const _Sliver({ - Key key, + Key? key, }) : super(key: key); @override @@ -229,7 +229,7 @@ class _Sliver extends StatelessWidget { class _SliverItem extends StatelessWidget { const _SliverItem({ - Key key, + Key? key, }) : super(key: key); @override diff --git a/test/sticky_test.dart b/test/sticky_test.dart index f855154..4300734 100644 --- a/test/sticky_test.dart +++ b/test/sticky_test.dart @@ -5,7 +5,7 @@ import 'package:flutter_sticky_header/flutter_sticky_header.dart'; void main() { setUp(() { - WidgetsBinding.instance.renderView.configuration = + WidgetsBinding.instance!.renderView.configuration = TestViewConfiguration(size: Size(400, 800)); }); @@ -171,8 +171,8 @@ void main() { class _Header extends StatelessWidget { const _Header({ - Key key, - @required this.index, + Key? key, + required this.index, }) : super(key: key); final int index; @@ -189,7 +189,7 @@ class _Header extends StatelessWidget { class _Sliver extends StatelessWidget { const _Sliver({ - Key key, + Key? key, }) : super(key: key); @override @@ -205,7 +205,7 @@ class _Sliver extends StatelessWidget { class _SliverItem extends StatelessWidget { const _SliverItem({ - Key key, + Key? key, }) : super(key: key); @override From a95e2ff955fa81d9184925cb396d66d018bbf357 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Wed, 7 Apr 2021 16:44:16 +0200 Subject: [PATCH 17/30] Migrate example --- example/lib/common.dart | 18 +++++++++--------- example/lib/examples/animated_header.dart | 14 +++++++------- example/lib/examples/grid.dart | 6 +++--- example/lib/examples/list.dart | 6 +++--- example/lib/examples/mix_slivers.dart | 6 +++--- example/lib/examples/not_sticky.dart | 6 +++--- example/lib/examples/reverse.dart | 6 +++--- example/lib/examples/side_header.dart | 10 +++++----- example/lib/main.dart | 18 +++++++++--------- example/pubspec.yaml | 4 ++-- 10 files changed, 47 insertions(+), 47 deletions(-) diff --git a/example/lib/common.dart b/example/lib/common.dart index 046d93e..b66a549 100644 --- a/example/lib/common.dart +++ b/example/lib/common.dart @@ -3,9 +3,9 @@ import 'package:flutter_sticky_header/flutter_sticky_header.dart'; class AppScaffold extends StatelessWidget { const AppScaffold({ - Key key, - @required this.title, - @required this.slivers, + Key? key, + required this.title, + required this.slivers, this.reverse = false, }) : super(key: key); @@ -32,7 +32,7 @@ class AppScaffold extends StatelessWidget { class _FloatingActionButton extends StatelessWidget { const _FloatingActionButton({ - Key key, + Key? key, }) : super(key: key); @override @@ -42,8 +42,8 @@ class _FloatingActionButton extends StatelessWidget { backgroundColor: Colors.green, onPressed: () { final double offset = - DefaultStickyHeaderController.of(context).stickyHeaderScrollOffset; - PrimaryScrollController.of(context).animateTo( + DefaultStickyHeaderController.of(context)!.stickyHeaderScrollOffset; + PrimaryScrollController.of(context)!.animateTo( offset, duration: Duration(milliseconds: 300), curve: Curves.easeIn, @@ -55,14 +55,14 @@ class _FloatingActionButton extends StatelessWidget { class Header extends StatelessWidget { const Header({ - Key key, + Key? key, this.index, this.title, this.color = Colors.lightBlue, }) : super(key: key); - final String title; - final int index; + final String? title; + final int? index; final Color color; @override diff --git a/example/lib/examples/animated_header.dart b/example/lib/examples/animated_header.dart index 01237e8..f624c57 100644 --- a/example/lib/examples/animated_header.dart +++ b/example/lib/examples/animated_header.dart @@ -5,7 +5,7 @@ import '../common.dart'; class AnimatedHeaderExample extends StatelessWidget { const AnimatedHeaderExample({ - Key key, + Key? key, }) : super(key: key); @override @@ -24,11 +24,11 @@ class AnimatedHeaderExample extends StatelessWidget { class _StickyHeaderList extends StatelessWidget { const _StickyHeaderList({ - Key key, + Key? key, this.index, }) : super(key: key); - final int index; + final int? index; @override Widget build(BuildContext context) { @@ -54,19 +54,19 @@ class _StickyHeaderList extends StatelessWidget { class _AnimatedHeader extends StatelessWidget { const _AnimatedHeader({ - Key key, + Key? key, this.state, this.index, }) : super(key: key); - final int index; - final SliverStickyHeaderState state; + final int? index; + final SliverStickyHeaderState? state; @override Widget build(BuildContext context) { return Header( index: index, - color: Colors.lightBlue.withOpacity(1 - state.scrollPercentage), + color: Colors.lightBlue.withOpacity(1 - state!.scrollPercentage), ); } } diff --git a/example/lib/examples/grid.dart b/example/lib/examples/grid.dart index 2b2312f..da1af75 100644 --- a/example/lib/examples/grid.dart +++ b/example/lib/examples/grid.dart @@ -5,7 +5,7 @@ import '../common.dart'; class GridExample extends StatelessWidget { const GridExample({ - Key key, + Key? key, }) : super(key: key); @override @@ -24,11 +24,11 @@ class GridExample extends StatelessWidget { class _StickyHeaderGrid extends StatelessWidget { const _StickyHeaderGrid({ - Key key, + Key? key, this.index, }) : super(key: key); - final int index; + final int? index; @override Widget build(BuildContext context) { diff --git a/example/lib/examples/list.dart b/example/lib/examples/list.dart index f94f841..27f5c42 100644 --- a/example/lib/examples/list.dart +++ b/example/lib/examples/list.dart @@ -5,7 +5,7 @@ import '../common.dart'; class ListExample extends StatelessWidget { const ListExample({ - Key key, + Key? key, }) : super(key: key); @override @@ -24,11 +24,11 @@ class ListExample extends StatelessWidget { class _StickyHeaderList extends StatelessWidget { const _StickyHeaderList({ - Key key, + Key? key, this.index, }) : super(key: key); - final int index; + final int? index; @override Widget build(BuildContext context) { diff --git a/example/lib/examples/mix_slivers.dart b/example/lib/examples/mix_slivers.dart index 698c352..ce4b8e3 100644 --- a/example/lib/examples/mix_slivers.dart +++ b/example/lib/examples/mix_slivers.dart @@ -5,7 +5,7 @@ import '../common.dart'; class MixSliversExample extends StatelessWidget { const MixSliversExample({ - Key key, + Key? key, }) : super(key: key); @override @@ -36,11 +36,11 @@ class MixSliversExample extends StatelessWidget { class _StickyHeaderList extends StatelessWidget { const _StickyHeaderList({ - Key key, + Key? key, this.index, }) : super(key: key); - final int index; + final int? index; @override Widget build(BuildContext context) { diff --git a/example/lib/examples/not_sticky.dart b/example/lib/examples/not_sticky.dart index af205f2..a6d7015 100644 --- a/example/lib/examples/not_sticky.dart +++ b/example/lib/examples/not_sticky.dart @@ -5,7 +5,7 @@ import '../common.dart'; class NotStickyExample extends StatelessWidget { const NotStickyExample({ - Key key, + Key? key, }) : super(key: key); @override @@ -24,11 +24,11 @@ class NotStickyExample extends StatelessWidget { class _NotStickyList extends StatelessWidget { const _NotStickyList({ - Key key, + Key? key, this.index, }) : super(key: key); - final int index; + final int? index; @override Widget build(BuildContext context) { diff --git a/example/lib/examples/reverse.dart b/example/lib/examples/reverse.dart index 9279f76..17fea56 100644 --- a/example/lib/examples/reverse.dart +++ b/example/lib/examples/reverse.dart @@ -5,7 +5,7 @@ import '../common.dart'; class ReverseExample extends StatelessWidget { const ReverseExample({ - Key key, + Key? key, }) : super(key: key); @override @@ -25,11 +25,11 @@ class ReverseExample extends StatelessWidget { class _StickyHeaderList extends StatelessWidget { const _StickyHeaderList({ - Key key, + Key? key, this.index, }) : super(key: key); - final int index; + final int? index; @override Widget build(BuildContext context) { diff --git a/example/lib/examples/side_header.dart b/example/lib/examples/side_header.dart index 8c6074e..a7602da 100644 --- a/example/lib/examples/side_header.dart +++ b/example/lib/examples/side_header.dart @@ -5,7 +5,7 @@ import '../common.dart'; class SideHeaderExample extends StatelessWidget { const SideHeaderExample({ - Key key, + Key? key, }) : super(key: key); @override @@ -24,11 +24,11 @@ class SideHeaderExample extends StatelessWidget { class _StickyHeaderGrid extends StatelessWidget { const _StickyHeaderGrid({ - Key key, + Key? key, this.index, }) : super(key: key); - final int index; + final int? index; @override Widget build(BuildContext context) { @@ -68,11 +68,11 @@ class _StickyHeaderGrid extends StatelessWidget { class _SideHeader extends StatelessWidget { const _SideHeader({ - Key key, + Key? key, this.index, }) : super(key: key); - final int index; + final int? index; @override Widget build(BuildContext context) { diff --git a/example/lib/main.dart b/example/lib/main.dart index a2cdd50..e1ddcc5 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,18 +1,18 @@ import 'package:flutter/material.dart'; -import 'examples/list.dart'; +import 'examples/animated_header.dart'; import 'examples/grid.dart'; +import 'examples/list.dart'; +import 'examples/mix_slivers.dart'; import 'examples/not_sticky.dart'; -import 'examples/side_header.dart'; -import 'examples/animated_header.dart'; import 'examples/reverse.dart'; -import 'examples/mix_slivers.dart'; +import 'examples/side_header.dart'; void main() => runApp(const App()); class App extends StatelessWidget { const App({ - Key key, + Key? key, }) : super(key: key); @override @@ -26,7 +26,7 @@ class App extends StatelessWidget { class _Home extends StatelessWidget { const _Home({ - Key key, + Key? key, }) : super(key: key); @override @@ -73,9 +73,9 @@ class _Home extends StatelessWidget { class _Item extends StatelessWidget { const _Item({ - Key key, - @required this.text, - @required this.builder, + Key? key, + required this.text, + required this.builder, }) : super(key: key); final String text; diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 04c5dc2..08a01a8 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.7.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' dependencies: flutter: @@ -29,7 +29,7 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^0.1.3 + cupertino_icons: ^1.0.2 dev_dependencies: flutter_test: From 11fbf0f4a9aab1df4e38d80670f12df4ff4a5a1e Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Wed, 7 Apr 2021 16:45:08 +0200 Subject: [PATCH 18/30] Update dependencies and bump version --- CHANGELOG.md | 6 ++++++ pubspec.yaml | 9 +++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 054bf2f..b50159b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.6.0 +### Changed +* Migrated to sound null-safety. +* Increase the minimum version of Flutter. +* Increase the minimum version of Dart SDK. + ## 0.5.0 ### Changed * The minimum version of Flutter. diff --git a/pubspec.yaml b/pubspec.yaml index 7ab42c6..cf73ff1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,15 +1,12 @@ name: flutter_sticky_header description: Flutter implementation of sticky headers as a sliver. Use it in a CustomScrollView. -version: 0.5.0 +version: 0.6.0 homepage: https://github.com/letsar/flutter_sticky_header dependencies: flutter: sdk: flutter - value_layout_builder: - git: - url: https://github.com/John-Dormevil/value_layout_builder.git - ref: null-safety + value_layout_builder: ^0.3.0 dev_dependencies: flutter_test: @@ -17,4 +14,4 @@ dev_dependencies: environment: sdk: '>=2.12.0 <3.0.0' - flutter: ">=1.20.0 <2.0.0" + flutter: '>=2.0.0 <3.0.0' From af51daf0ff20a0ae963b9c0b1b2b17e9513f5acc Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Sat, 10 Apr 2021 21:18:23 +0200 Subject: [PATCH 19/30] Lowered flutter requirement --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index cf73ff1..80312a8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,4 +14,4 @@ dev_dependencies: environment: sdk: '>=2.12.0 <3.0.0' - flutter: '>=2.0.0 <3.0.0' + flutter: '>=1.24.0-10.2.pre <3.0.0' From 97f2695617dd9009a9fb4c0862c48ac2a8112c48 Mon Sep 17 00:00:00 2001 From: Nico Mexis Date: Sat, 10 Apr 2021 21:34:16 +0200 Subject: [PATCH 20/30] Resolve discussions --- lib/src/rendering/sliver_sticky_header.dart | 2 +- lib/src/widgets/sliver_sticky_header.dart | 4 ++-- pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index bc0a513..a1c300a 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -122,7 +122,7 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { return result; } - double? computeHeaderExtent() { + double computeHeaderExtent() { if (header == null) return 0.0; assert(header!.hasSize); switch (constraints.axis) { diff --git a/lib/src/widgets/sliver_sticky_header.dart b/lib/src/widgets/sliver_sticky_header.dart index 6a98f6c..48d0657 100644 --- a/lib/src/widgets/sliver_sticky_header.dart +++ b/lib/src/widgets/sliver_sticky_header.dart @@ -166,7 +166,7 @@ class SliverStickyHeader extends RenderObjectWidget { /// [DefaultStickyHeaderController.of] will be used. SliverStickyHeader.builder({ Key? key, - SliverStickyHeaderWidgetBuilder? builder, + required SliverStickyHeaderWidgetBuilder builder, Widget? sliver, bool overlapsContent: false, bool sticky = true, @@ -175,7 +175,7 @@ class SliverStickyHeader extends RenderObjectWidget { key: key, header: ValueLayoutBuilder( builder: (context, constraints) => - builder!(context, constraints.value), + builder(context, constraints.value), ), sliver: sliver, overlapsContent: overlapsContent, diff --git a/pubspec.yaml b/pubspec.yaml index 80312a8..8443fb4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,4 +14,4 @@ dev_dependencies: environment: sdk: '>=2.12.0 <3.0.0' - flutter: '>=1.24.0-10.2.pre <3.0.0' + flutter: '>=1.24.0-10.2.pre' From 9aa3c58bdd92b828651118313b1710228caba863 Mon Sep 17 00:00:00 2001 From: Amit Kotlovski Date: Thu, 22 Apr 2021 11:07:27 +0300 Subject: [PATCH 21/30] Fixing null-safety error with SliverStickyHeaderState --- lib/src/rendering/sliver_sticky_header.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index a1c300a..c813340 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -271,8 +271,8 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { if (_oldState != state) { _oldState = state; header!.layout( - BoxValueConstraints( - value: _oldState, + BoxValueConstraints( + value: _oldState!, constraints: constraints.asBoxConstraints(), ), parentUsesSize: true, From 3b532b5918053c8e62e03cd81a124aeece0d9bbc Mon Sep 17 00:00:00 2001 From: Amit Kotlovski Date: Thu, 22 Apr 2021 11:15:19 +0300 Subject: [PATCH 22/30] Advancing version to 0.6.1 --- CHANGELOG.md | 4 ++++ pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b50159b..c63738e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.1 +### Fixed +* Error due to null-safety migration. + ## 0.6.0 ### Changed * Migrated to sound null-safety. diff --git a/pubspec.yaml b/pubspec.yaml index 8443fb4..aa2c736 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_sticky_header description: Flutter implementation of sticky headers as a sliver. Use it in a CustomScrollView. -version: 0.6.0 +version: 0.6.1 homepage: https://github.com/letsar/flutter_sticky_header dependencies: From c2e5b3fb00ebb88df75cf1c1cfe94c8bb86217d3 Mon Sep 17 00:00:00 2001 From: Romain Rastel Date: Tue, 1 Mar 2022 12:40:33 +0100 Subject: [PATCH 23/30] upgrade example android target --- example/analysis_options.yaml | 29 +++++++++++++++++++ example/android/.gitignore | 2 ++ example/android/app/build.gradle | 19 +++++++----- .../android/app/src/main/AndroidManifest.xml | 19 ++---------- .../res/drawable-v21/launch_background.xml | 12 ++++++++ .../app/src/main/res/values-night/styles.xml | 18 ++++++++++++ .../app/src/main/res/values/styles.xml | 10 +++---- example/android/build.gradle | 8 ++--- example/android/gradle.properties | 1 - .../gradle/wrapper/gradle-wrapper.properties | 2 +- 10 files changed, 86 insertions(+), 34 deletions(-) create mode 100644 example/analysis_options.yaml create mode 100644 example/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 example/android/app/src/main/res/values-night/styles.xml diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 0000000..61b6c4d --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/example/android/.gitignore b/example/android/.gitignore index 0a741cb..6f56801 100644 --- a/example/android/.gitignore +++ b/example/android/.gitignore @@ -9,3 +9,5 @@ GeneratedPluginRegistrant.java # Remember to never publicly share your keystore. # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app key.properties +**/*.keystore +**/*.jks diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 32feb18..ce52202 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -26,21 +26,26 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 28 + compileSdkVersion flutter.compileSdkVersion - sourceSets { - main.java.srcDirs += 'src/main/kotlin' + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' } - lintOptions { - disable 'InvalidPackage' + sourceSets { + main.java.srcDirs += 'src/main/kotlin' } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.letsar.sh.example" - minSdkVersion 16 - targetSdkVersion 28 + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index d401f8e..ef91f0f 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,16 +1,12 @@ - - - - diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..3db14bb --- /dev/null +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml index 1f83a33..d460d1e 100644 --- a/example/android/app/src/main/res/values/styles.xml +++ b/example/android/app/src/main/res/values/styles.xml @@ -1,7 +1,7 @@ - - diff --git a/example/android/build.gradle b/example/android/build.gradle index 3100ad2..4256f91 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.3.50' + ext.kotlin_version = '1.6.10' repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:4.1.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -14,7 +14,7 @@ buildscript { allprojects { repositories { google() - jcenter() + mavenCentral() } } diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 38c8d45..94adc3a 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,4 +1,3 @@ org.gradle.jvmargs=-Xmx1536M -android.enableR8=true android.useAndroidX=true android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 296b146..bc6a58a 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip From 605bc8ce8d524ce2e4c0501936b34f5406f52ac6 Mon Sep 17 00:00:00 2001 From: Romain Rastel Date: Tue, 10 May 2022 18:30:56 +0200 Subject: [PATCH 24/30] fixes not sticky hit test --- example/lib/common.dart | 21 ++++++++++------- example/lib/examples/list.dart | 3 +++ example/lib/examples/not_sticky.dart | 3 +++ example/lib/main.dart | 6 ++++- lib/src/rendering/sliver_sticky_header.dart | 25 +++++++++++++++------ 5 files changed, 42 insertions(+), 16 deletions(-) diff --git a/example/lib/common.dart b/example/lib/common.dart index b66a549..60c52e8 100644 --- a/example/lib/common.dart +++ b/example/lib/common.dart @@ -67,14 +67,19 @@ class Header extends StatelessWidget { @override Widget build(BuildContext context) { - return Container( - height: 60, - color: color, - padding: EdgeInsets.symmetric(horizontal: 16.0), - alignment: Alignment.centerLeft, - child: Text( - title ?? 'Header #$index', - style: const TextStyle(color: Colors.white), + return GestureDetector( + onTap: () { + print('hit $index'); + }, + child: Container( + height: 60, + color: color, + padding: EdgeInsets.symmetric(horizontal: 16.0), + alignment: Alignment.centerLeft, + child: Text( + title ?? 'Header #$index', + style: const TextStyle(color: Colors.white), + ), ), ); } diff --git a/example/lib/examples/list.dart b/example/lib/examples/list.dart index 27f5c42..f127bac 100644 --- a/example/lib/examples/list.dart +++ b/example/lib/examples/list.dart @@ -37,6 +37,9 @@ class _StickyHeaderList extends StatelessWidget { sliver: SliverList( delegate: SliverChildBuilderDelegate( (context, i) => ListTile( + onTap: () { + print('tile $i'); + }, leading: CircleAvatar( child: Text('$index'), ), diff --git a/example/lib/examples/not_sticky.dart b/example/lib/examples/not_sticky.dart index a6d7015..3ad8405 100644 --- a/example/lib/examples/not_sticky.dart +++ b/example/lib/examples/not_sticky.dart @@ -38,6 +38,9 @@ class _NotStickyList extends StatelessWidget { sliver: SliverList( delegate: SliverChildBuilderDelegate( (context, i) => ListTile( + onTap: () { + print('tile $i'); + }, leading: CircleAvatar( child: Text('$index'), ), diff --git a/example/lib/main.dart b/example/lib/main.dart index e1ddcc5..43cc2f9 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'examples/animated_header.dart'; import 'examples/grid.dart'; @@ -8,7 +9,10 @@ import 'examples/not_sticky.dart'; import 'examples/reverse.dart'; import 'examples/side_header.dart'; -void main() => runApp(const App()); +void main() { + // debugPaintPointersEnabled = true; + runApp(const App()); +} class App extends StatelessWidget { const App({ diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index c813340..30265ed 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -303,14 +303,25 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { bool hitTestChildren(SliverHitTestResult result, {required double mainAxisPosition, required double crossAxisPosition}) { assert(geometry!.hitTestExtent > 0.0); + final double childScrollExtent = child?.geometry?.scrollExtent ?? 0.0; + final double headerPosition = sticky + ? math.min( + constraints.overlap, + childScrollExtent - + constraints.scrollOffset - + (overlapsContent ? _headerExtent! : 0.0)) + : -constraints.scrollOffset; + if (header != null && - mainAxisPosition - constraints.overlap <= _headerExtent!) { - return hitTestBoxChild( - BoxHitTestResult.wrap(SliverHitTestResult.wrap(result)), - header!, - mainAxisPosition: mainAxisPosition - constraints.overlap, - crossAxisPosition: crossAxisPosition, - ) || + (mainAxisPosition - headerPosition) <= _headerExtent!) { + final didHitHeader = hitTestBoxChild( + BoxHitTestResult.wrap(SliverHitTestResult.wrap(result)), + header!, + mainAxisPosition: childMainAxisPosition(header), + crossAxisPosition: crossAxisPosition, + ); + + return didHitHeader || (_overlapsContent && child != null && child!.geometry!.hitTestExtent > 0.0 && From f29a9b61fa7501862c4d10a9aede6f1bd23baf54 Mon Sep 17 00:00:00 2001 From: Romain Rastel Date: Tue, 10 May 2022 18:31:46 +0200 Subject: [PATCH 25/30] bump version 0.6.2 --- CHANGELOG.md | 4 ++++ pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c63738e..dc40e4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.2 +### Fixed +* Hit Test on not sticky header + ## 0.6.1 ### Fixed * Error due to null-safety migration. diff --git a/pubspec.yaml b/pubspec.yaml index aa2c736..04e80a1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_sticky_header description: Flutter implementation of sticky headers as a sliver. Use it in a CustomScrollView. -version: 0.6.1 +version: 0.6.2 homepage: https://github.com/letsar/flutter_sticky_header dependencies: From b030be33a2fe306861a2e19531ccda4829eb103c Mon Sep 17 00:00:00 2001 From: Romain Rastel Date: Wed, 25 May 2022 09:59:31 +0200 Subject: [PATCH 26/30] fixes issue #75 --- CHANGELOG.md | 4 + example/lib/main_issue_75.dart | 144 ++++++++++++++++++++ lib/src/rendering/sliver_sticky_header.dart | 2 +- pubspec.yaml | 2 +- 4 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 example/lib/main_issue_75.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index dc40e4a..ed2413d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.3 +### Fixed +* Hit Test on not sticky header + ## 0.6.2 ### Fixed * Hit Test on not sticky header diff --git a/example/lib/main_issue_75.dart b/example/lib/main_issue_75.dart new file mode 100644 index 0000000..a30f1cd --- /dev/null +++ b/example/lib/main_issue_75.dart @@ -0,0 +1,144 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_sticky_header/flutter_sticky_header.dart'; + +void main() { + runApp(MaterialApp(home: TabHeaderExample())); +} + +class TabHeaderExample extends StatefulWidget { + const TabHeaderExample({Key? key}) : super(key: key); + + @override + State createState() => _TabHeaderExampleState(); +} + +class _TabHeaderExampleState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Tab List Example'), + ), + body: CustomScrollView( + slivers: [ + _StickyHeaderList(index: 0), + _StickyHeaderList(index: 1), + _StickyHeaderList(index: 2), + _StickyHeaderList(index: 3), + ], + ), + ); + } +} + +class _StickyHeaderList extends StatefulWidget { + final int index; + const _StickyHeaderList({Key? key, required this.index}) : super(key: key); + + @override + State<_StickyHeaderList> createState() => _StickyHeaderListState(); +} + +class _StickyHeaderListState extends State<_StickyHeaderList> + with TickerProviderStateMixin { + late TabController _tabController; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 10, vsync: this); + } + + @override + Widget build(BuildContext context) { + return SliverStickyHeader( + header: Container( + color: Colors.white, + height: 47, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: TabBar( + isScrollable: true, + controller: _tabController, + indicatorColor: const Color(0xFF216DDF), + indicatorSize: TabBarIndicatorSize.label, + indicatorWeight: 2, + indicatorPadding: EdgeInsets.only(bottom: 10), + unselectedLabelColor: const Color(0xFF888888), + unselectedLabelStyle: TextStyle(fontSize: 17), + labelColor: const Color(0xFF216DDF), + labelStyle: + TextStyle(fontSize: 17, fontWeight: FontWeight.bold), + tabs: _createTabs(), + onTap: (index) { + print(index); + }, + ), + ), + InkWell( + child: Container( + decoration: BoxDecoration( + color: Colors.red, + boxShadow: [ + BoxShadow( + color: Colors.white, + blurRadius: 8, + offset: Offset(-20, 0), + ), + ], + ), + padding: EdgeInsets.only(left: 5, right: 12), + child: Row( + children: [ + Text( + "全部", + style: TextStyle( + color: const Color(0xFF101010), fontSize: 17), + ), + ], + ), + ), + onTap: () { + // Can not response + print("1234567"); + }, + ), + ], + ), + ), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, i) => ListTile( + onTap: () { + print('tile $i'); + }, + leading: CircleAvatar( + child: Text('${widget.index}'), + ), + title: Text('List tile #$i'), + ), + childCount: 6, + ), + ), + ); + } + + /// 创建Tabs + List _createTabs() { + return [ + "我是tab", + "我是tab", + "我是tab", + "我是tab", + "我是tab", + "我是tab", + "我是tab", + "我是tab", + "我是tab", + "我是tab" + ].map((e) => Tab(text: e)).toList(); + } +} diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index 30265ed..34ae60a 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -317,7 +317,7 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { final didHitHeader = hitTestBoxChild( BoxHitTestResult.wrap(SliverHitTestResult.wrap(result)), header!, - mainAxisPosition: childMainAxisPosition(header), + mainAxisPosition: mainAxisPosition - childMainAxisPosition(header), crossAxisPosition: crossAxisPosition, ); diff --git a/pubspec.yaml b/pubspec.yaml index 04e80a1..fbc131b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_sticky_header description: Flutter implementation of sticky headers as a sliver. Use it in a CustomScrollView. -version: 0.6.2 +version: 0.6.3 homepage: https://github.com/letsar/flutter_sticky_header dependencies: From 8d571f5c3198dc5a75153fedda36ca24381d8246 Mon Sep 17 00:00:00 2001 From: Romain Rastel Date: Wed, 13 Jul 2022 18:39:39 +0200 Subject: [PATCH 27/30] fixes nested sticky headers --- CHANGELOG.md | 4 + example/analysis_options.yaml | 29 ---- example/ios/Flutter/AppFrameworkInfo.plist | 2 +- example/ios/Runner.xcodeproj/project.pbxproj | 10 +- .../contents.xcworkspacedata | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- example/ios/Runner/Info.plist | 2 + example/lib/examples/nested.dart | 124 ++++++++++++++++ example/lib/main.dart | 6 +- example/pubspec.yaml | 3 +- lib/src/rendering/sliver_sticky_header.dart | 5 +- pubspec.yaml | 3 +- test/controller_test.dart | 5 +- test/sticky_test.dart | 132 +++++++++++++++++- 14 files changed, 281 insertions(+), 48 deletions(-) delete mode 100644 example/analysis_options.yaml create mode 100644 example/lib/examples/nested.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index ed2413d..2034efc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.4 +### Fixed +* Not passing right overlap contraints to sliver child. Now we can create nested sticky headers! + ## 0.6.3 ### Fixed * Hit Test on not sticky header diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml deleted file mode 100644 index 61b6c4d..0000000 --- a/example/analysis_options.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. -include: package:flutter_lints/flutter.yaml - -linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at - # https://dart-lang.github.io/linter/lints/index.html. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. - rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 6b4c0f7..f2872cf 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 9.0 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index d7a4911..2109967 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -127,7 +127,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -272,7 +272,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -354,7 +354,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -403,7 +403,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; 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.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140c..3db53b6 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + diff --git a/example/lib/examples/nested.dart b/example/lib/examples/nested.dart new file mode 100644 index 0000000..4affc58 --- /dev/null +++ b/example/lib/examples/nested.dart @@ -0,0 +1,124 @@ +import 'package:example/common.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_sticky_header/flutter_sticky_header.dart'; +import 'package:sliver_tools/sliver_tools.dart'; + +class NestedExample extends StatelessWidget { + const NestedExample({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return AppScaffold( + title: 'Nested Sticky Headers', + slivers: [ + SliverStickyHeader( + header: Header(title: '1'), + sliver: MultiSliver( + children: [ + SliverStickyHeader( + header: Header(title: '1.1'), + sliver: _SliverLeaf(), + ), + SliverStickyHeader( + header: Header(title: '1.2'), + sliver: MultiSliver( + children: [ + SliverStickyHeader( + header: Header(title: '1.2.1'), + sliver: _SliverLeaf(), + ), + SliverStickyHeader( + header: Header(title: '1.2.2'), + sliver: _SliverLeaf(), + ), + SliverStickyHeader( + header: Header(title: '1.2.3'), + sliver: _SliverLeaf(), + ), + ], + ), + ), + SliverStickyHeader( + header: Header(title: '1.3'), + sliver: _SliverLeaf(), + ), + ], + ), + ), + SliverStickyHeader( + header: Header(title: '2'), + sliver: MultiSliver( + children: [ + SliverStickyHeader( + header: Header(title: '2.1'), + sliver: _SliverLeaf(), + ), + SliverStickyHeader( + header: Header(title: '2.2'), + sliver: MultiSliver( + children: [ + SliverStickyHeader( + header: Header(title: '2.2.1'), + sliver: _SliverLeaf(), + ), + SliverStickyHeader( + header: Header(title: '2.2.2'), + sliver: _SliverLeaf(), + ), + SliverStickyHeader( + header: Header(title: '2.2.3'), + sliver: _SliverLeaf(), + ), + ], + ), + ), + SliverStickyHeader( + header: Header(title: '2.3'), + sliver: _SliverLeaf(), + ), + ], + ), + ), + SliverStickyHeader( + header: Header(title: '3'), + sliver: _SliverLeaf(), + ), + SliverStickyHeader( + header: Header(title: '4'), + sliver: MultiSliver( + children: [ + SliverStickyHeader( + header: Header(title: '4.1'), + sliver: _SliverLeaf(), + ), + SliverStickyHeader( + header: Header(title: '4.2'), + sliver: _SliverLeaf(), + ), + SliverStickyHeader( + header: Header(title: '4.3'), + sliver: _SliverLeaf(), + ), + ], + ), + ), + ], + ); + } +} + +class _SliverLeaf extends StatelessWidget { + const _SliverLeaf(); + + @override + Widget build(BuildContext context) { + return SliverToBoxAdapter( + child: Container( + height: 200, + color: Colors.amber, + ), + ); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index 43cc2f9..339dff2 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,5 +1,5 @@ +import 'package:example/examples/nested.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'examples/animated_header.dart'; import 'examples/grid.dart'; @@ -69,6 +69,10 @@ class _Home extends StatelessWidget { text: 'Mixing other slivers', builder: (_) => const MixSliversExample(), ), + _Item( + text: 'Nested sticky headers', + builder: (_) => const NestedExample(), + ), ], ), ); diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 08a01a8..903f267 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=2.17.0 <3.0.0' dependencies: flutter: @@ -30,6 +30,7 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 + sliver_tools: ^0.2.7 dev_dependencies: flutter_test: diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index 34ae60a..3166698 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -1,6 +1,5 @@ import 'dart:math' as math; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_sticky_header/flutter_sticky_header.dart'; @@ -178,7 +177,8 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { constraints.copyWith( scrollOffset: math.max(0.0, constraints.scrollOffset - headerExtent), cacheOrigin: math.min(0.0, constraints.cacheOrigin + headerExtent), - overlap: 0.0, + overlap: math.min(headerExtent, constraints.scrollOffset) + + (sticky ? constraints.overlap : 0), remainingPaintExtent: constraints.remainingPaintExtent - headerPaintExtent, remainingCacheExtent: @@ -203,6 +203,7 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { geometry = SliverGeometry( scrollExtent: headerExtent + childLayoutGeometry.scrollExtent, + maxScrollObstructionExtent: sticky ? headerPaintExtent : 0, paintExtent: paintExtent, layoutExtent: math.min( headerPaintExtent + childLayoutGeometry.layoutExtent, paintExtent), diff --git a/pubspec.yaml b/pubspec.yaml index fbc131b..bfa2b5a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_sticky_header description: Flutter implementation of sticky headers as a sliver. Use it in a CustomScrollView. -version: 0.6.3 +version: 0.6.4 homepage: https://github.com/letsar/flutter_sticky_header dependencies: @@ -11,6 +11,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + sliver_tools: ^0.2.7 environment: sdk: '>=2.12.0 <3.0.0' diff --git a/test/controller_test.dart b/test/controller_test.dart index 827a979..d5ee534 100644 --- a/test/controller_test.dart +++ b/test/controller_test.dart @@ -1,11 +1,10 @@ import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - import 'package:flutter_sticky_header/flutter_sticky_header.dart'; +import 'package:flutter_test/flutter_test.dart'; void main() { setUp(() { - WidgetsBinding.instance!.renderView.configuration = + WidgetsBinding.instance.renderView.configuration = TestViewConfiguration(size: Size(400, 800)); }); diff --git a/test/sticky_test.dart b/test/sticky_test.dart index 4300734..402f34e 100644 --- a/test/sticky_test.dart +++ b/test/sticky_test.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - import 'package:flutter_sticky_header/flutter_sticky_header.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:sliver_tools/sliver_tools.dart'; void main() { setUp(() { - WidgetsBinding.instance!.renderView.configuration = + WidgetsBinding.instance.renderView.configuration = TestViewConfiguration(size: Size(400, 800)); }); @@ -167,6 +167,132 @@ void main() { tester.getRect(header01Finder), const Rect.fromLTRB(0, 800, 400, 880)); expect(header02Finder, findsNothing); }); + + testWidgets('Testing multi-depth sticky headers', (tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: CustomScrollView( + cacheExtent: 0, + slivers: [ + SliverStickyHeader( + header: _HierarchyHeader(hierarchy: '1'), + sliver: MultiSliver( + children: [ + SliverStickyHeader( + header: _HierarchyHeader(hierarchy: '1.1'), + sliver: const _Sliver100(), + ), + SliverStickyHeader( + header: _HierarchyHeader(hierarchy: '1.2'), + sliver: MultiSliver( + children: [ + SliverStickyHeader( + header: _HierarchyHeader(hierarchy: '1.2.1'), + sliver: const _Sliver100(), + ), + SliverStickyHeader( + header: _HierarchyHeader(hierarchy: '1.2.2'), + sliver: const _Sliver100(), + ), + ], + ), + ), + ], + ), + ), + SliverStickyHeader( + header: _HierarchyHeader(hierarchy: '2'), + sliver: const _Sliver100(), + ), + SliverStickyHeader( + header: _HierarchyHeader(hierarchy: '3'), + sliver: MultiSliver( + children: [ + SliverStickyHeader( + header: _HierarchyHeader(hierarchy: '3.1'), + sliver: const _Sliver100(), + ), + SliverStickyHeader( + header: _HierarchyHeader(hierarchy: '3.2'), + sliver: const _Sliver100(), + ), + ], + ), + ), + ], + ), + ), + ), + ); + + final header001Finder = find.text('Header 1'); + final header011Finder = find.text('Header 1.1'); + final header012Finder = find.text('Header 1.2'); + final header121Finder = find.text('Header 1.2.1'); + + expect(header001Finder, findsOneWidget); + expect(header011Finder, findsOneWidget); + expect(header012Finder, findsOneWidget); + expect(header121Finder, findsOneWidget); + + expect(tester.getTopLeft(header011Finder).dy, 50); + expect(tester.getTopLeft(header012Finder).dy, 200); + expect(tester.getTopLeft(header121Finder).dy, 250); + + // We scroll a little and expect that header 1 is sticky. + final gesture = await tester.startGesture(Offset(200, 100)); + await gesture.moveBy(Offset(0, -25)); + await tester.pump(); + + expect(tester.getTopLeft(header011Finder).dy, 50); + expect(tester.getTopLeft(header012Finder).dy, 175); + expect(tester.getTopLeft(header121Finder).dy, 225); + + await gesture.moveBy(Offset(0, -125)); + await tester.pump(); + + expect(tester.getTopLeft(header011Finder).dy, 0); + expect(tester.getTopLeft(header012Finder).dy, 50); + expect(tester.getTopLeft(header121Finder).dy, 100); + + await gesture.moveBy(Offset(0, -25)); + await tester.pump(); + + expect(tester.getTopLeft(header012Finder).dy, 50); + expect(tester.getTopLeft(header121Finder).dy, 100); + }); +} + +class _HierarchyHeader extends StatelessWidget { + const _HierarchyHeader({ + Key? key, + required this.hierarchy, + }) : super(key: key); + + final String hierarchy; + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.blue, + child: Text('Header $hierarchy'), + height: 50, + ); + } +} + +class _Sliver100 extends StatelessWidget { + const _Sliver100({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return SliverToBoxAdapter( + child: SizedBox(height: 100), + ); + } } class _Header extends StatelessWidget { From c5f289af05914f480bca9331ed5d8abaff4c4df5 Mon Sep 17 00:00:00 2001 From: charlesrmajor Date: Tue, 19 Jul 2022 13:09:01 -0600 Subject: [PATCH 28/30] added reverse --- example/lib/examples/reverse2.dart | 52 +++++++++++++++++++++ example/lib/main.dart | 5 ++ lib/src/rendering/sliver_sticky_header.dart | 27 +++++++++-- lib/src/widgets/sliver_sticky_header.dart | 9 ++++ 4 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 example/lib/examples/reverse2.dart diff --git a/example/lib/examples/reverse2.dart b/example/lib/examples/reverse2.dart new file mode 100644 index 0000000..ee0f73e --- /dev/null +++ b/example/lib/examples/reverse2.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_sticky_header/flutter_sticky_header.dart'; + +import '../common.dart'; + +class ReverseExample2 extends StatelessWidget { + const ReverseExample2({ + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return AppScaffold( + reverse: true, + title: 'Reverse Example', + slivers: [ + _StickyHeaderList(index: 0), + _StickyHeaderList(index: 1), + _StickyHeaderList(index: 2), + _StickyHeaderList(index: 3), + ], + ); + } +} + +class _StickyHeaderList extends StatelessWidget { + const _StickyHeaderList({ + Key? key, + this.index, + }) : super(key: key); + + final int? index; + + @override + Widget build(BuildContext context) { + return SliverStickyHeader( + header: Header(index: index), + reverse: true, + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (context, i) => ListTile( + leading: CircleAvatar( + child: Text('$index'), + ), + title: Text('List tile #$i'), + ), + childCount: 6, + ), + ), + ); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index 339dff2..bc624db 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -7,6 +7,7 @@ import 'examples/list.dart'; import 'examples/mix_slivers.dart'; import 'examples/not_sticky.dart'; import 'examples/reverse.dart'; +import 'examples/reverse2.dart'; import 'examples/side_header.dart'; void main() { @@ -65,6 +66,10 @@ class _Home extends StatelessWidget { text: 'Reverse List Example', builder: (_) => const ReverseExample(), ), + _Item( + text: 'Reverse List Example 2', + builder: (_) => const ReverseExample2(), + ), _Item( text: 'Mixing other slivers', builder: (_) => const MixSliversExample(), diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index 3166698..6f73ac6 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -14,11 +14,14 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { RenderObject? header, RenderSliver? child, bool overlapsContent: false, + bool reverse: false, bool sticky: true, StickyHeaderController? controller, }) : _overlapsContent = overlapsContent, _sticky = sticky, - _controller = controller { + _controller = controller, + _reverse = reverse + { this.header = header as RenderBox?; this.child = child; } @@ -36,6 +39,16 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { markNeedsLayout(); } + bool get reverse => _reverse; + bool _reverse; + set reverse(bool value) { + assert(value != null); + if (_reverse == value) return; + _reverse = value; + markNeedsLayout(); + } + + bool get sticky => _sticky; bool _sticky; @@ -145,6 +158,8 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { AxisDirection axisDirection = applyGrowthDirectionToAxisDirection( constraints.axisDirection, constraints.growthDirection); + if (reverse) axisDirection = flipAxisDirection(axisDirection); + if (header != null) { header!.layout( BoxValueConstraints( @@ -178,7 +193,7 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { scrollOffset: math.max(0.0, constraints.scrollOffset - headerExtent), cacheOrigin: math.min(0.0, constraints.cacheOrigin + headerExtent), overlap: math.min(headerExtent, constraints.scrollOffset) + - (sticky ? constraints.overlap : 0), + (sticky ? constraints.overlap : 0), // TODO: this was just '0' remainingPaintExtent: constraints.remainingPaintExtent - headerPaintExtent, remainingCacheExtent: @@ -219,18 +234,22 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { final SliverPhysicalParentData? childParentData = child!.parentData as SliverPhysicalParentData?; + + final double childStartOffset = + reverse ? headerExtent : headerPaintExtent; + switch (axisDirection) { case AxisDirection.up: childParentData!.paintOffset = Offset.zero; break; case AxisDirection.right: childParentData!.paintOffset = Offset( - calculatePaintOffset(constraints, from: 0.0, to: headerExtent), + calculatePaintOffset(constraints, from: 0.0, to: childStartOffset), 0.0); break; case AxisDirection.down: childParentData!.paintOffset = Offset(0.0, - calculatePaintOffset(constraints, from: 0.0, to: headerExtent)); + calculatePaintOffset(constraints, from: 0.0, to: childStartOffset)); break; case AxisDirection.left: childParentData!.paintOffset = Offset.zero; diff --git a/lib/src/widgets/sliver_sticky_header.dart b/lib/src/widgets/sliver_sticky_header.dart index 48d0657..7a9e372 100644 --- a/lib/src/widgets/sliver_sticky_header.dart +++ b/lib/src/widgets/sliver_sticky_header.dart @@ -154,6 +154,7 @@ class SliverStickyHeader extends RenderObjectWidget { this.sliver, this.overlapsContent: false, this.sticky = true, + this.reverse = false, this.controller, }) : super(key: key); @@ -197,6 +198,8 @@ class SliverStickyHeader extends RenderObjectWidget { /// Defaults to true. final bool sticky; + final bool reverse; + /// The controller used to interact with this sliver. /// /// If a [StickyHeaderController] is not provided, then the value of [DefaultStickyHeaderController.of] @@ -208,6 +211,7 @@ class SliverStickyHeader extends RenderObjectWidget { return RenderSliverStickyHeader( overlapsContent: overlapsContent, sticky: sticky, + reverse: reverse, controller: controller ?? DefaultStickyHeaderController.of(context), ); } @@ -224,6 +228,7 @@ class SliverStickyHeader extends RenderObjectWidget { renderObject ..overlapsContent = overlapsContent ..sticky = sticky + ..reverse = reverse ..controller = controller ?? DefaultStickyHeaderController.of(context); } } @@ -249,6 +254,7 @@ class SliverStickyHeaderBuilder extends StatelessWidget { this.sliver, this.overlapsContent: false, this.sticky = true, + this.reverse = false, this.controller, }) : super(key: key); @@ -269,6 +275,8 @@ class SliverStickyHeaderBuilder extends StatelessWidget { /// Defaults to true. final bool sticky; + final bool reverse; + /// The controller used to interact with this sliver. /// /// If a [StickyHeaderController] is not provided, then the value of [DefaultStickyHeaderController.of] @@ -281,6 +289,7 @@ class SliverStickyHeaderBuilder extends StatelessWidget { overlapsContent: overlapsContent, sliver: sliver, sticky: sticky, + reverse: reverse, controller: controller, header: ValueLayoutBuilder( builder: (context, constraints) => builder(context, constraints.value), From 9446db79769790cd009293b030cbb06666eff24e Mon Sep 17 00:00:00 2001 From: Charles Major Date: Tue, 19 Jul 2022 14:38:32 -0600 Subject: [PATCH 29/30] added reverse --- lib/src/rendering/sliver_sticky_header.dart | 35 +++++++++++++-------- lib/src/widgets/sliver_sticky_header.dart | 2 ++ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index 6f73ac6..0a07b04 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -41,6 +41,7 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { bool get reverse => _reverse; bool _reverse; + set reverse(bool value) { assert(value != null); if (_reverse == value) return; @@ -158,7 +159,7 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { AxisDirection axisDirection = applyGrowthDirectionToAxisDirection( constraints.axisDirection, constraints.growthDirection); - if (reverse) axisDirection = flipAxisDirection(axisDirection); + if (_reverse) axisDirection = flipAxisDirection(axisDirection); if (header != null) { header!.layout( @@ -236,21 +237,23 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { child!.parentData as SliverPhysicalParentData?; final double childStartOffset = - reverse ? headerExtent : headerPaintExtent; + _reverse ? headerExtent : headerExtent; switch (axisDirection) { case AxisDirection.up: childParentData!.paintOffset = Offset.zero; break; + case AxisDirection.down: + childParentData!.paintOffset = Offset(0.0, + calculatePaintOffset(constraints, from: 0.0, to: childStartOffset)); + // calculatePaintOffset(constraints, from: 0.0, to: reverse ? -childStartOffset : childStartOffset)); + break; + case AxisDirection.right: childParentData!.paintOffset = Offset( calculatePaintOffset(constraints, from: 0.0, to: childStartOffset), 0.0); break; - case AxisDirection.down: - childParentData!.paintOffset = Offset(0.0, - calculatePaintOffset(constraints, from: 0.0, to: childStartOffset)); - break; case AxisDirection.left: childParentData!.paintOffset = Offset.zero; break; @@ -302,12 +305,14 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { switch (axisDirection) { case AxisDirection.up: + double headerOffset = - headerPosition - _headerExtent!; headerParentData!.paintOffset = Offset( - 0.0, geometry!.paintExtent - headerPosition - _headerExtent!); + 0.0, geometry!.paintExtent + (_reverse ? -headerOffset : headerOffset) ); break; case AxisDirection.down: headerParentData!.paintOffset = Offset(0.0, headerPosition); break; + case AxisDirection.left: headerParentData!.paintOffset = Offset( geometry!.paintExtent - headerPosition - _headerExtent!, 0.0); @@ -329,7 +334,8 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { constraints.overlap, childScrollExtent - constraints.scrollOffset - - (overlapsContent ? _headerExtent! : 0.0)) + // (overlapsContent ? _headerExtent! : 0.0)) + (((overlapsContent && !_reverse) || (!overlapsContent && _reverse)) ? _headerExtent! : 0.0)) : -constraints.scrollOffset; if (header != null && @@ -337,7 +343,8 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { final didHitHeader = hitTestBoxChild( BoxHitTestResult.wrap(SliverHitTestResult.wrap(result)), header!, - mainAxisPosition: mainAxisPosition - childMainAxisPosition(header), + // mainAxisPosition: mainAxisPosition - childMainAxisPosition(header), + mainAxisPosition: mainAxisPosition - (_reverse ? 0 : childMainAxisPosition(header)), crossAxisPosition: crossAxisPosition, ); @@ -347,11 +354,13 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { child!.geometry!.hitTestExtent > 0.0 && child!.hitTest(result, mainAxisPosition: - mainAxisPosition - childMainAxisPosition(child), + // mainAxisPosition - childMainAxisPosition(child), + mainAxisPosition - (_reverse ? 0 : childMainAxisPosition(child)), crossAxisPosition: crossAxisPosition)); } else if (child != null && child!.geometry!.hitTestExtent > 0.0) { return child!.hitTest(result, - mainAxisPosition: mainAxisPosition - childMainAxisPosition(child), + // mainAxisPosition: mainAxisPosition - childMainAxisPosition(child), + mainAxisPosition: mainAxisPosition - (_reverse ? 0 : childMainAxisPosition(child)), crossAxisPosition: crossAxisPosition); } return false; @@ -392,14 +401,14 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { if (child != null && child!.geometry!.visible) { final SliverPhysicalParentData childParentData = child!.parentData as SliverPhysicalParentData; - context.paintChild(child!, offset + childParentData.paintOffset); + context.paintChild(child!, offset + childParentData.paintOffset*(_reverse ? 1 : 1)); } // The header must be drawn over the sliver. if (header != null) { final SliverPhysicalParentData headerParentData = header!.parentData as SliverPhysicalParentData; - context.paintChild(header!, offset + headerParentData.paintOffset); + context.paintChild(header!, offset + headerParentData.paintOffset*(_reverse ? -1 : 1)); } } } diff --git a/lib/src/widgets/sliver_sticky_header.dart b/lib/src/widgets/sliver_sticky_header.dart index 7a9e372..9c0c78d 100644 --- a/lib/src/widgets/sliver_sticky_header.dart +++ b/lib/src/widgets/sliver_sticky_header.dart @@ -171,6 +171,7 @@ class SliverStickyHeader extends RenderObjectWidget { Widget? sliver, bool overlapsContent: false, bool sticky = true, + bool reverse = false, StickyHeaderController? controller, }) : this( key: key, @@ -181,6 +182,7 @@ class SliverStickyHeader extends RenderObjectWidget { sliver: sliver, overlapsContent: overlapsContent, sticky: sticky, + reverse: reverse, controller: controller, ); From f13c1df4726a1495bb74a7bf6d429ef06c52e11e Mon Sep 17 00:00:00 2001 From: Charles Major Date: Thu, 21 Jul 2022 12:55:46 -0600 Subject: [PATCH 30/30] added reverse --- lib/src/rendering/sliver_sticky_header.dart | 117 ++++++++++---------- lib/src/widgets/sliver_sticky_header.dart | 5 +- 2 files changed, 59 insertions(+), 63 deletions(-) diff --git a/lib/src/rendering/sliver_sticky_header.dart b/lib/src/rendering/sliver_sticky_header.dart index 0a07b04..3ff58e4 100644 --- a/lib/src/rendering/sliver_sticky_header.dart +++ b/lib/src/rendering/sliver_sticky_header.dart @@ -2,7 +2,8 @@ import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import 'package:flutter_sticky_header/flutter_sticky_header.dart'; +// import 'package:flutter_sticky_header/flutter_sticky_header.dart'; +import '../../flutter_sticky_header.dart'; import 'package:value_layout_builder/value_layout_builder.dart'; /// A sliver with a [RenderBox] as header and a [RenderSliver] as child. @@ -49,7 +50,6 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { markNeedsLayout(); } - bool get sticky => _sticky; bool _sticky; @@ -148,6 +148,14 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { double? get headerLogicalExtent => overlapsContent ? 0.0 : _headerExtent; + double get headerPosition => sticky + ? math.min( + constraints.overlap, + (child?.geometry?.scrollExtent ?? 0.0) - + constraints.scrollOffset - + (overlapsContent ? _headerExtent! : 0.0)) + : -constraints.scrollOffset; + @override void performLayout() { if (header == null && child == null) { @@ -155,12 +163,9 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { return; } - // One of them is not null. AxisDirection axisDirection = applyGrowthDirectionToAxisDirection( constraints.axisDirection, constraints.growthDirection); - if (_reverse) axisDirection = flipAxisDirection(axisDirection); - if (header != null) { header!.layout( BoxValueConstraints( @@ -193,8 +198,7 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { constraints.copyWith( scrollOffset: math.max(0.0, constraints.scrollOffset - headerExtent), cacheOrigin: math.min(0.0, constraints.cacheOrigin + headerExtent), - overlap: math.min(headerExtent, constraints.scrollOffset) + - (sticky ? constraints.overlap : 0), // TODO: this was just '0' + overlap: math.min(headerExtent, constraints.scrollOffset), remainingPaintExtent: constraints.remainingPaintExtent - headerPaintExtent, remainingCacheExtent: @@ -236,22 +240,23 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { final SliverPhysicalParentData? childParentData = child!.parentData as SliverPhysicalParentData?; - final double childStartOffset = - _reverse ? headerExtent : headerExtent; - switch (axisDirection) { case AxisDirection.up: - childParentData!.paintOffset = Offset.zero; + // this was working ... but maybe this is getting in the way of what we should be re-positioning + if (_reverse) childParentData!.paintOffset = Offset(0.0,-headerExtent); + else childParentData!.paintOffset = Offset.zero; // reverse break; case AxisDirection.down: - childParentData!.paintOffset = Offset(0.0, - calculatePaintOffset(constraints, from: 0.0, to: childStartOffset)); - // calculatePaintOffset(constraints, from: 0.0, to: reverse ? -childStartOffset : childStartOffset)); + if (_reverse) childParentData!.paintOffset = Offset(0.0, + -headerExtent); + else + childParentData!.paintOffset = Offset(0.0, + calculatePaintOffset(constraints, from: 0.0, to: headerExtent)); break; case AxisDirection.right: childParentData!.paintOffset = Offset( - calculatePaintOffset(constraints, from: 0.0, to: childStartOffset), + calculatePaintOffset(constraints, from: 0.0, to: headerExtent), 0.0); break; case AxisDirection.left: @@ -263,19 +268,14 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { if (header != null) { final SliverPhysicalParentData? headerParentData = header!.parentData as SliverPhysicalParentData?; - final double childScrollExtent = child?.geometry?.scrollExtent ?? 0.0; - final double headerPosition = sticky - ? math.min( - constraints.overlap, - childScrollExtent - - constraints.scrollOffset - - (overlapsContent ? _headerExtent! : 0.0)) - : -constraints.scrollOffset; - - _isPinned = sticky && - ((constraints.scrollOffset + constraints.overlap) > 0.0 || - constraints.remainingPaintExtent == - constraints.viewportMainAxisExtent); + + _isPinned = () { + if (!sticky) return false; + if (_reverse) return (constraints.remainingPaintExtent < (constraints.viewportMainAxisExtent - ((child!.parentData as SliverPhysicalParentData?)?.paintOffset.distance ?? 0))); + else return ((constraints.scrollOffset + constraints.overlap) > 0.0 || + constraints.remainingPaintExtent == + constraints.viewportMainAxisExtent); + }(); final double headerScrollRatio = ((headerPosition - constraints.overlap).abs() / _headerExtent!); @@ -306,13 +306,14 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { switch (axisDirection) { case AxisDirection.up: double headerOffset = - headerPosition - _headerExtent!; - headerParentData!.paintOffset = Offset( - 0.0, geometry!.paintExtent + (_reverse ? -headerOffset : headerOffset) ); + if (_reverse) headerParentData!.paintOffset = Offset(0.0, 0.0 + + (constraints.remainingPaintExtent < _headerExtent! ? (geometry!.paintExtent + headerOffset) + : 0)); + else headerParentData!.paintOffset = Offset(0.0, geometry!.paintExtent +headerOffset); break; case AxisDirection.down: - headerParentData!.paintOffset = Offset(0.0, headerPosition); + headerParentData?.paintOffset = Offset(0.0, headerPosition); break; - case AxisDirection.left: headerParentData!.paintOffset = Offset( geometry!.paintExtent - headerPosition - _headerExtent!, 0.0); @@ -328,23 +329,13 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { bool hitTestChildren(SliverHitTestResult result, {required double mainAxisPosition, required double crossAxisPosition}) { assert(geometry!.hitTestExtent > 0.0); - final double childScrollExtent = child?.geometry?.scrollExtent ?? 0.0; - final double headerPosition = sticky - ? math.min( - constraints.overlap, - childScrollExtent - - constraints.scrollOffset - - // (overlapsContent ? _headerExtent! : 0.0)) - (((overlapsContent && !_reverse) || (!overlapsContent && _reverse)) ? _headerExtent! : 0.0)) - : -constraints.scrollOffset; - if (header != null && - (mainAxisPosition - headerPosition) <= _headerExtent!) { + (geometry!.paintExtent - mainAxisPosition < _headerExtent!) + ) { final didHitHeader = hitTestBoxChild( BoxHitTestResult.wrap(SliverHitTestResult.wrap(result)), header!, - // mainAxisPosition: mainAxisPosition - childMainAxisPosition(header), - mainAxisPosition: mainAxisPosition - (_reverse ? 0 : childMainAxisPosition(header)), + mainAxisPosition: geometry!.paintExtent - mainAxisPosition + childMainAxisPosition(header), crossAxisPosition: crossAxisPosition, ); @@ -354,13 +345,12 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { child!.geometry!.hitTestExtent > 0.0 && child!.hitTest(result, mainAxisPosition: - // mainAxisPosition - childMainAxisPosition(child), - mainAxisPosition - (_reverse ? 0 : childMainAxisPosition(child)), + mainAxisPosition, crossAxisPosition: crossAxisPosition)); } else if (child != null && child!.geometry!.hitTestExtent > 0.0) { + print('testing child'); return child!.hitTest(result, - // mainAxisPosition: mainAxisPosition - childMainAxisPosition(child), - mainAxisPosition: mainAxisPosition - (_reverse ? 0 : childMainAxisPosition(child)), + mainAxisPosition: mainAxisPosition, crossAxisPosition: crossAxisPosition); } return false; @@ -368,13 +358,15 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { @override double childMainAxisPosition(RenderObject? child) { - if (child == header) - return _isPinned + if (child == header) { + if (_isPinned) return 0; + if (!_reverse) return _isPinned ? 0.0 : -(constraints.scrollOffset + constraints.overlap); - if (child == this.child) - return calculatePaintOffset(constraints, - from: 0.0, to: headerLogicalExtent!); + return (constraints.scrollOffset + constraints.overlap); + } else if (child == this.child) { + return calculatePaintOffset(constraints,from: 0.0, to: headerLogicalExtent!); + } return 0; } @@ -382,7 +374,11 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { double? childScrollOffset(RenderObject child) { assert(child.parent == this); if (child == this.child) { - return _headerExtent; + // if (_reverse) + return 0; + // return _headerExtent; + // } else if (_reverse && child == this._header) { + // return _headerExtent; } else { return super.childScrollOffset(child); } @@ -398,17 +394,16 @@ class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { @override void paint(PaintingContext context, Offset offset) { if (geometry!.visible) { + final Offset? childParentDataOffset = (child == null) ? null : (child!.parentData as SliverPhysicalParentData).paintOffset; + final Offset? headerParentDataOffset = (header == null) ? null : (header!.parentData as SliverPhysicalParentData).paintOffset; + if (child != null && child!.geometry!.visible) { - final SliverPhysicalParentData childParentData = - child!.parentData as SliverPhysicalParentData; - context.paintChild(child!, offset + childParentData.paintOffset*(_reverse ? 1 : 1)); + context.paintChild(child!, offset + (_reverse ? -childParentDataOffset! : childParentDataOffset!)); } // The header must be drawn over the sliver. if (header != null) { - final SliverPhysicalParentData headerParentData = - header!.parentData as SliverPhysicalParentData; - context.paintChild(header!, offset + headerParentData.paintOffset*(_reverse ? -1 : 1)); + context.paintChild(header!, offset + headerParentDataOffset!); } } } diff --git a/lib/src/widgets/sliver_sticky_header.dart b/lib/src/widgets/sliver_sticky_header.dart index 9c0c78d..7372f80 100644 --- a/lib/src/widgets/sliver_sticky_header.dart +++ b/lib/src/widgets/sliver_sticky_header.dart @@ -1,6 +1,7 @@ +import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import 'package:flutter/widgets.dart'; -import 'package:flutter_sticky_header/src/rendering/sliver_sticky_header.dart'; +// import 'package:flutter_sticky_header/src/rendering/sliver_sticky_header.dart'; +import '../rendering/sliver_sticky_header.dart'; import 'package:value_layout_builder/value_layout_builder.dart'; /// Signature used by [SliverStickyHeader.builder] to build the header