From 8674e454775d230c0ffa5b75fb4d528daf11b5dc Mon Sep 17 00:00:00 2001 From: Daniel Coyula Date: Tue, 23 Jan 2024 13:37:01 -0500 Subject: [PATCH 1/8] Setup Flutter package structure --- .gitignore | 1 + pubspec.lock | 188 --------------------------------------------------- pubspec.yaml | 16 +++-- 3 files changed, 13 insertions(+), 192 deletions(-) delete mode 100644 pubspec.lock diff --git a/.gitignore b/.gitignore index 29a3a50..ebebf35 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ migrate_working_dir/ .pub-cache/ .pub/ /build/ +/pubspec.lock # Symbolication related app.*.symbols diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index 8e26fe5..0000000 --- a/pubspec.lock +++ /dev/null @@ -1,188 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.dev" - source: hosted - version: "1.18.0" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d - url: "https://pub.dev" - source: hosted - version: "1.0.6" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 - url: "https://pub.dev" - source: hosted - version: "3.0.1" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - lints: - dependency: transitive - description: - name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 - url: "https://pub.dev" - source: hosted - version: "3.0.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" - url: "https://pub.dev" - source: hosted - version: "0.12.16" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" - url: "https://pub.dev" - source: hosted - version: "0.5.0" - meta: - dependency: transitive - description: - name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e - url: "https://pub.dev" - source: hosted - version: "1.10.0" - path: - dependency: transitive - description: - name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" - url: "https://pub.dev" - source: hosted - version: "1.8.3" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.dev" - source: hosted - version: "1.11.1" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" - source: hosted - version: "2.1.2" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" - url: "https://pub.dev" - source: hosted - version: "0.6.1" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - web: - dependency: transitive - description: - name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 - url: "https://pub.dev" - source: hosted - version: "0.3.0" -sdks: - dart: ">=3.2.3 <4.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index b9e3972..930f2a1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,8 +1,16 @@ name: flutter_elastic_list_view -description: "A new Flutter project." -publish_to: 'none' - -version: 1.0.0+1 +description: "Elastic ListView for Flutter" +repository: https://github.com/monster555/flutter_elastic_list_view +version: 0.0.1 +homepage: https://github.com/monster555/flutter_elastic_list_view + +platforms: + android: + ios: + linux: + macos: + windows: + web: environment: sdk: '>=3.2.3 <4.0.0' From ba6f0219c0cde904dfcb43a9e8fa2906d830f8ab Mon Sep 17 00:00:00 2001 From: Daniel Coyula Date: Tue, 23 Jan 2024 13:37:30 -0500 Subject: [PATCH 2/8] Update README with package details --- README.md | 86 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 76 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 975ed79..7c69607 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,82 @@ -# flutter_elastic_list_view +# ElasticListView -A new Flutter project. +## Description -## Getting Started +`ElasticListView` is a drop-in replacement for Flutter's `ListView`, providing an elastic overscroll effect. It adds the `.builder` and `.separated` constructors as well as the standard `ListView` constructor. To migrate to `ElasticListView`, simply add the `Elastic` prefix to any existing `ListView` widget. -This project is a starting point for a Flutter application. +!ElasticListView Demo -A few resources to get you started if this is your first Flutter project: +## Features -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) +- **Elastic Overscroll Effect**: `ElasticListView` enhances the user experience by providing an elastic overscroll effect. +- **Drag to Scroll**: Adds the drag to scroll behavior by default, providing a smooth scrolling experience. This can be disabled by setting the `enableDragScrolling` property to false. +- **Optimized Performance**: Leverages the performance optimizations of the standard `ListView`, ensuring excellent performance. -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +## Extended Functionality with Full Compatibility + +`ElasticListView` maintains the exact same properties as the standard `ListView`, ensuring full compatibility and making it a seamless replacement. In addition, it introduces new properties to control the elastic effect, offering enhanced functionality and customization options beyond the standard `ListView`. + +## New Properties + +Here are the new properties introduced by `ElasticListView`: + +- `curve`: The curve to apply when animating the elastic effect. Defaults to `Curves.easeOut`. +- `animationDuration`: The duration of the overscroll bounce animation. Defaults to `Duration(milliseconds: 200)`. +- `enableDragScrolling`: Whether to enable drag scrolling. Defaults to `true`. +- `elasticityFactor`: The factor by which the scroll view overscrolls. Defaults to `4`. + +Each of these properties allows you to customize the behavior of the elastic effect in `ElasticListView`. + +## Usage + +To use `ElasticListView`, simply replace your existing `ListView` with `ElasticListView`. All the properties are the same, ensuring full compatibility. + +For `ListView`: + +```dart +ElasticListView( + children: [ + ListTile( + leading: Icon(Icons.map), + title: Text('Map'), + ), + ListTile( + leading: Icon(Icons.photo_album), + title: Text('Album'), + ), + ListTile( + leading: Icon(Icons.phone), + title: Text('Phone'), + ), + ], +) +``` + +For `ListView.builder`: + +```dart +ElasticListView.builder( + itemCount: 10, + itemBuilder: (context, index) { + return ListTile( + title: Text('Item $index'), + ); + }, +) +``` + +And for `ListView.separated`: + +```dart +ElasticListView.separated( + itemCount: 10, + separatorBuilder: (BuildContext context, int index) => Divider(), + itemBuilder: (BuildContext context, int index) { + return ListTile( + title: Text('Item $index'), + ); + }, +) +``` +## Contribution +Contributions are welcome! If you encounter any issues or have suggestions for improvements, please feel free to create a pull request. \ No newline at end of file From ab37acf18a9a0d5937498f632e0394c50c5bdb17 Mon Sep 17 00:00:00 2001 From: Daniel Coyula Date: Tue, 23 Jan 2024 13:39:33 -0500 Subject: [PATCH 3/8] Convert app to Flutter package --- lib/elastic_list_view.dart | 3 + .../drag_scroll_behavior_configuration.dart | 0 lib/src/ui/elastic_list_view.dart | 679 ++++++++++++++++++ .../utils/elastic_list_view_utils.dart | 0 lib/ui/elastic_list_view.dart | 333 --------- 5 files changed, 682 insertions(+), 333 deletions(-) create mode 100644 lib/elastic_list_view.dart rename lib/{ => src}/config/drag_scroll_behavior_configuration.dart (100%) create mode 100644 lib/src/ui/elastic_list_view.dart rename lib/{ => src}/utils/elastic_list_view_utils.dart (100%) delete mode 100644 lib/ui/elastic_list_view.dart diff --git a/lib/elastic_list_view.dart b/lib/elastic_list_view.dart new file mode 100644 index 0000000..20bd452 --- /dev/null +++ b/lib/elastic_list_view.dart @@ -0,0 +1,3 @@ +library flutter_elastic_list_view; + +export 'src/ui/elastic_list_view.dart'; diff --git a/lib/config/drag_scroll_behavior_configuration.dart b/lib/src/config/drag_scroll_behavior_configuration.dart similarity index 100% rename from lib/config/drag_scroll_behavior_configuration.dart rename to lib/src/config/drag_scroll_behavior_configuration.dart diff --git a/lib/src/ui/elastic_list_view.dart b/lib/src/ui/elastic_list_view.dart new file mode 100644 index 0000000..af033ff --- /dev/null +++ b/lib/src/ui/elastic_list_view.dart @@ -0,0 +1,679 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter_elastic_list_view/src/config/drag_scroll_behavior_configuration.dart'; +import 'package:flutter_elastic_list_view/src/utils/elastic_list_view_utils.dart'; + +/// A customizable [ListView] with an elastic effect. +/// +/// This widget provides an extended [ListView] with additional features such as +/// elastic scrolling, customizable animation curve, and duration. +/// +/// The [ElasticListView] allows you to customize various aspects, including +/// scroll direction, controller, item extent, itemBuilder, and more. +class ElasticListView extends StatefulWidget { + /// The axis along which the [ElasticListView] scrolls. + final Axis scrollDirection; + + /// Whether the [ElasticListView] scrolls in the reverse direction. + final bool reverse; + + /// An optional controller for the [ElasticListView]. + final ScrollController? controller; + + /// Whether this [ElasticListView] is the primary scroll view associated with the parent. + final bool? primary; + + /// The physics of the [ElasticListView]. + final ScrollPhysics? physics; + + /// Whether the [ElasticListView] should wrap its content or not. + final bool shrinkWrap; + + /// The padding for the [ElasticListView]. + final EdgeInsetsGeometry? padding; + + /// The size of each item in the [ElasticListView]. + final double? itemExtent; + + /// A builder that returns the size of each item in the [ElasticListView]. + final ItemExtentBuilder? itemExtentBuilder; + + /// A prototype of the item that is used for measuring its size. + final Widget? prototypeItem; + + /// Called to build children for the [ElasticListView]. + final IndexedWidgetBuilder itemBuilder; + + /// Callback to find the index of a child based on its key. + final ChildIndexGetter? findChildIndexCallback; + + /// The number of children that the [ElasticListView] should contain. + final int? itemCount; + + /// Whether to add automatic keep-alive widgets to the children of the [ElasticListView]. + final bool addAutomaticKeepAlives; + + /// Whether to add repaint boundaries to the children of the [ElasticListView]. + final bool addRepaintBoundaries; + + /// Whether to add semantic indexes to the children of the [ElasticListView]. + final bool addSemanticIndexes; + + /// The extent of the cache in pixels. + final double? cacheExtent; + + /// The number of children that are taken into account for semantic indexing. + final int? semanticChildCount; + + /// The drag start behavior for the [ElasticListView]. + final DragStartBehavior dragStartBehavior; + + /// The keyboard dismissal behavior for the [ElasticListView]. + final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; + + /// An identifier to use for state restoration. + final String? restorationId; + + /// The clip behavior for the [ElasticListView]. + final Clip clipBehavior; + + /// The curve for the elastic scrolling animation. + final Curve curve; + + /// The duration of the elastic scrolling animation. + final Duration animationDuration; + + /// Enables drag scrolling behavior for the [ElasticListView]. + final bool enableDragScrolling; + + /// The elasticity factor for the [ElasticListView]. + final int elasticityFactor; + + /// A builder that returns a separator widget for the [ElasticListView]. + final IndexedWidgetBuilder? separatorBuilder; + + /// List of children for the [ElasticListView]. + final List? children; + + /// [ElasticListView._base] is a private constructor that provides a base for + /// the other constructors of the [ElasticListView] widget. + /// + /// This constructor is not meant to be directly invoked. Instead, it's used + /// internally by the other constructors ([ElasticListView], [ElasticListView.builder], + /// and [ElasticListView.separated]) to provide the core functionality of the + /// [ElasticListView] widget. + /// + /// The [itemBuilder` function is called with the current context and index, + /// and it should return a widget that corresponds to the item at that index + /// in the list. + /// + /// The [itemCount` is the number of items in the list and must be provided. + /// + /// The [elasticityFactor` controls the elasticity of the dynamic padding effect + /// during scrolling. + /// + /// The [separatorBuilder] function is similar to [itemBuilder], but it's used + /// for creating separator widgets in the [ElasticListView.separated] constructor. + /// + /// This constructor also includes assertions to ensure that the [elasticityFactor] + /// is greater than or equal to 0, and the [animationDuration] is greater than + /// or equal to 100 ms for optimal user experience. + const ElasticListView._base({ + super.key, + this.scrollDirection = Axis.vertical, + this.reverse = false, + this.controller, + this.primary, + this.physics, + this.shrinkWrap = false, + this.padding, + this.itemExtent, + this.itemExtentBuilder, + this.prototypeItem, + required this.itemBuilder, + this.children, + this.findChildIndexCallback, + required this.itemCount, + this.addAutomaticKeepAlives = true, + this.addRepaintBoundaries = true, + this.addSemanticIndexes = true, + this.cacheExtent, + this.semanticChildCount, + this.dragStartBehavior = DragStartBehavior.start, + this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, + this.restorationId, + this.clipBehavior = Clip.hardEdge, + this.curve = Curves.easeOut, + this.animationDuration = const Duration(milliseconds: 200), + this.enableDragScrolling = true, + this.elasticityFactor = 4, + this.separatorBuilder, + }) : assert(elasticityFactor >= 0, + 'Elasticity factor must be greater than or equal to 0.'), + assert(animationDuration >= const Duration(milliseconds: 100), + 'Animation duration should be greater than or equal to 100 ms for optimal user experience.'); + + /// [ElasticListView] is a customizable list view widget that provides a unique + /// scrolling experience. + /// + /// Unlike a standard [ListView], [ElasticListView] adds dynamic padding when + /// scrolling, which varies depending on the scroll speed. This is controlled + /// by the [elasticityFactor] parameter. + /// + /// The [curve] and [animationDuration] parameters allow you to customize the + /// animation of the dynamic padding effect. + /// + /// Here's an example of how to use [ElasticListView] + /// ```dart + /// ElasticListView( + /// children: List.generate( + /// 50, + /// (index) { + /// return Container( + /// height: 100, + /// margin: const EdgeInsets.all(8), + /// decoration: BoxDecoration( + /// color: Colors.red[(index % 9 + 1) * 100], + /// borderRadius: BorderRadius.circular(16), + /// ), + /// ); + /// }, + /// ), + /// ) + /// ``` + ElasticListView({ + Key? key, + Axis scrollDirection = Axis.vertical, + bool reverse = false, + ScrollController? controller, + bool? primary, + ScrollPhysics? physics, + bool shrinkWrap = false, + EdgeInsetsGeometry? padding, + double? itemExtent, + ItemExtentBuilder? itemExtentBuilder, + Widget? prototypeItem, + required List children, + ChildIndexGetter? findChildIndexCallback, + bool addAutomaticKeepAlives = true, + bool addRepaintBoundaries = true, + bool addSemanticIndexes = true, + double? cacheExtent, + int? semanticChildCount, + DragStartBehavior dragStartBehavior = DragStartBehavior.start, + ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = + ScrollViewKeyboardDismissBehavior.manual, + String? restorationId, + Clip clipBehavior = Clip.hardEdge, + Curve curve = Curves.easeOut, + Duration animationDuration = const Duration(milliseconds: 200), + bool enableDragScrolling = true, + int elasticityFactor = 4, + }) : this._base( + key: key, + scrollDirection: scrollDirection, + reverse: reverse, + controller: controller, + primary: primary, + physics: physics, + shrinkWrap: shrinkWrap, + padding: padding, + itemExtent: itemExtent, + itemExtentBuilder: itemExtentBuilder, + prototypeItem: prototypeItem, + itemBuilder: (_, int index) => children[index], + findChildIndexCallback: findChildIndexCallback, + itemCount: children.length, + addAutomaticKeepAlives: addAutomaticKeepAlives, + addRepaintBoundaries: addRepaintBoundaries, + addSemanticIndexes: addSemanticIndexes, + cacheExtent: cacheExtent, + semanticChildCount: semanticChildCount, + dragStartBehavior: dragStartBehavior, + keyboardDismissBehavior: keyboardDismissBehavior, + restorationId: restorationId, + clipBehavior: clipBehavior, + curve: curve, + animationDuration: animationDuration, + enableDragScrolling: enableDragScrolling, + elasticityFactor: elasticityFactor, + ); + + /// [ElasticListView.builder] is a constructor that creates a list view with + /// items that are created on demand. + /// + /// This constructor is suitable for list views with a large (or infinite) number + /// of children because the builder is called only for those children that are + /// actually visible. + /// + /// The [itemBuilder] function is called with the current context and index, + /// and it should return a widget that corresponds to the item at that index + /// in the list. + /// + /// The [itemCount] is the number of items in the list and must be provided. + /// + /// The [elasticityFactor] controls the elasticity of the dynamic padding + /// effect during scrolling. + /// + /// Here's an example of how to use [ElasticListView.builder] + /// ```dart + /// ElasticListView.builder( + /// itemCount: 50, + /// itemBuilder: (context, index) { + /// return Container( + /// height: 100, + /// margin: const EdgeInsets.all(8), + /// decoration: BoxDecoration( + /// color: Colors.green[(index % 9 + 1) * 100], + /// borderRadius: BorderRadius.circular(16), + /// ), + /// ); + /// }, + /// ) + /// ``` + const ElasticListView.builder({ + Key? key, + Axis scrollDirection = Axis.vertical, + bool reverse = false, + ScrollController? controller, + bool? primary, + ScrollPhysics? physics, + bool shrinkWrap = false, + EdgeInsetsGeometry? padding, + double? itemExtent, + ItemExtentBuilder? itemExtentBuilder, + Widget? prototypeItem, + required IndexedWidgetBuilder itemBuilder, + ChildIndexGetter? findChildIndexCallback, + required int itemCount, + bool addAutomaticKeepAlives = true, + bool addRepaintBoundaries = true, + bool addSemanticIndexes = true, + double? cacheExtent, + int? semanticChildCount, + DragStartBehavior dragStartBehavior = DragStartBehavior.start, + ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = + ScrollViewKeyboardDismissBehavior.manual, + String? restorationId, + Clip clipBehavior = Clip.hardEdge, + Curve curve = Curves.easeOut, + Duration animationDuration = const Duration(milliseconds: 200), + bool enableDragScrolling = true, + int elasticityFactor = 4, + }) : this._base( + key: key, + scrollDirection: scrollDirection, + reverse: reverse, + controller: controller, + primary: primary, + physics: physics, + shrinkWrap: shrinkWrap, + padding: padding, + itemExtent: itemExtent, + itemExtentBuilder: itemExtentBuilder, + prototypeItem: prototypeItem, + itemBuilder: itemBuilder, + findChildIndexCallback: findChildIndexCallback, + itemCount: itemCount, + addAutomaticKeepAlives: addAutomaticKeepAlives, + addRepaintBoundaries: addRepaintBoundaries, + addSemanticIndexes: addSemanticIndexes, + cacheExtent: cacheExtent, + semanticChildCount: semanticChildCount, + dragStartBehavior: dragStartBehavior, + keyboardDismissBehavior: keyboardDismissBehavior, + restorationId: restorationId, + clipBehavior: clipBehavior, + curve: curve, + animationDuration: animationDuration, + enableDragScrolling: enableDragScrolling, + elasticityFactor: elasticityFactor, + ); + + /// [ElasticListView.separated] is a constructor that creates a list view with + /// items and separators that are created on demand. + /// + /// This constructor is suitable for list views with a large number of children + /// as the builder is called only for those children that are actually visible. + /// + /// The [itemBuilder] function is called with the current context and index, + /// and it should return a widget that corresponds to the item at that index in + /// the list. + /// + /// The [separatorBuilder] function is similar to [itemBuilder], but it's used + /// for creating separator widgets. + /// + /// The [itemCount] is the number of items in the list and must be provided. + /// + /// The [elasticityFactor] controls the elasticity of the dynamic padding effect + /// during scrolling. + /// + /// Here's an example of how to use [ElasticListView.separated] + /// ```dart + /// ElasticListView.separated( + /// itemCount: 50, + /// separatorBuilder: (context, index) => const Divider(), + /// itemBuilder: (context, index) { + /// return Container( + /// height: 100, + /// margin: const EdgeInsets.all(8), + /// decoration: BoxDecoration( + /// color: Colors.blue[(index % 9 + 1) * 100], + /// borderRadius: BorderRadius.circular(16), + /// ), + /// ); + /// }, + /// ) + /// ``` + const ElasticListView.separated({ + Key? key, + Axis scrollDirection = Axis.vertical, + bool reverse = false, + ScrollController? controller, + bool? primary, + ScrollPhysics? physics, + bool shrinkWrap = false, + EdgeInsetsGeometry? padding, + double? itemExtent, + ItemExtentBuilder? itemExtentBuilder, + Widget? prototypeItem, + required IndexedWidgetBuilder itemBuilder, + ChildIndexGetter? findChildIndexCallback, + required int itemCount, + bool addAutomaticKeepAlives = true, + bool addRepaintBoundaries = true, + bool addSemanticIndexes = true, + double? cacheExtent, + int? semanticChildCount, + DragStartBehavior dragStartBehavior = DragStartBehavior.start, + ScrollViewKeyboardDismissBehavior keyboardDismissBehavior = + ScrollViewKeyboardDismissBehavior.manual, + String? restorationId, + Clip clipBehavior = Clip.hardEdge, + Curve curve = Curves.easeOut, + Duration animationDuration = const Duration(milliseconds: 200), + bool enableDragScrolling = true, + int elasticityFactor = 4, + required IndexedWidgetBuilder separatorBuilder, + }) : this._base( + key: key, + scrollDirection: scrollDirection, + reverse: reverse, + controller: controller, + primary: primary, + physics: physics, + shrinkWrap: shrinkWrap, + padding: padding, + itemExtent: itemExtent, + itemExtentBuilder: itemExtentBuilder, + prototypeItem: prototypeItem, + itemBuilder: itemBuilder, + findChildIndexCallback: findChildIndexCallback, + itemCount: itemCount, + addAutomaticKeepAlives: addAutomaticKeepAlives, + addRepaintBoundaries: addRepaintBoundaries, + addSemanticIndexes: addSemanticIndexes, + cacheExtent: cacheExtent, + semanticChildCount: semanticChildCount, + dragStartBehavior: dragStartBehavior, + keyboardDismissBehavior: keyboardDismissBehavior, + restorationId: restorationId, + clipBehavior: clipBehavior, + curve: curve, + animationDuration: animationDuration, + enableDragScrolling: enableDragScrolling, + elasticityFactor: elasticityFactor, + separatorBuilder: separatorBuilder, + ); + + @override + ElasticListViewState createState() => ElasticListViewState(); +} + +class ElasticListViewState extends State + with SingleTickerProviderStateMixin { + /// Animation controller for managing elastic overscroll animation. + late AnimationController _controller; + + /// Scroll controller for the underlying list view. + late ScrollController _scrollController; + + /// Current elasticity factor for overscroll behavior. + double _elasticity = 1.0; + + /// Previous scroll offset to calculate scroll speed. + double _previousOffset = 0.0; + + /// Timestamp of the previous scroll update. + DateTime _previousTimestamp = DateTime.now(); + + /// Getter for the current elasticity factor. + double get elasticity => _elasticity; + + ScrollController get scrollController => _scrollController; + + @override + void initState() { + super.initState(); + + _controller = AnimationController( + vsync: this, + duration: widget.animationDuration, + ); + + _scrollController = widget.controller ?? ScrollController(); + _scrollController.addListener(_handleScroll); + } + + @override + void dispose() { + // Dispose of the animation and scroll controllers to release resources. + _controller.dispose(); + _scrollController.dispose(); + super.dispose(); + } + + /// Padding for the [ElasticListView] based on the current elasticity. + /// + /// This method calculates the padding for the [ElasticListView] based on the + /// current elasticity. It uses the `widget.scrollDirection` to determine the + /// padding for the vertical or horizontal axis. + EdgeInsets get _padding => widget.scrollDirection == Axis.vertical + ? EdgeInsets.symmetric( + vertical: (elasticity - 1) * widget.elasticityFactor) + : EdgeInsets.symmetric( + horizontal: (elasticity - 1) * widget.elasticityFactor); + + @override + Widget build(BuildContext context) { + return widget.separatorBuilder != null + ? ScrollConfiguration( + behavior: widget.enableDragScrolling + ? DragScrollBehavior() + : const ScrollBehavior(), + child: NotificationListener( + onNotification: _notificationHandler, + child: ListView.separated( + scrollDirection: widget.scrollDirection, + reverse: widget.reverse, + controller: _scrollController, + primary: widget.primary, + physics: widget.physics, + shrinkWrap: widget.shrinkWrap, + padding: widget.padding, + itemCount: widget.itemCount!, + itemBuilder: (context, index) { + return AnimatedPadding( + duration: widget.animationDuration, + curve: widget.curve, + padding: _padding, + child: widget.itemBuilder(context, index), + ); + }, + separatorBuilder: (context, index) { + return AnimatedPadding( + duration: widget.animationDuration, + curve: widget.curve, + padding: _padding, + child: widget.separatorBuilder!(context, index), + ); + }, + addAutomaticKeepAlives: widget.addAutomaticKeepAlives, + addRepaintBoundaries: widget.addRepaintBoundaries, + addSemanticIndexes: widget.addSemanticIndexes, + cacheExtent: widget.cacheExtent, + dragStartBehavior: widget.dragStartBehavior, + keyboardDismissBehavior: widget.keyboardDismissBehavior, + restorationId: widget.restorationId, + clipBehavior: widget.clipBehavior, + ), + ), + ) + : ScrollConfiguration( + behavior: widget.enableDragScrolling + ? DragScrollBehavior() + : const ScrollBehavior(), + child: NotificationListener( + onNotification: _notificationHandler, + child: ListView.builder( + scrollDirection: widget.scrollDirection, + reverse: widget.reverse, + controller: _scrollController, + primary: widget.primary, + physics: widget.physics, + shrinkWrap: widget.shrinkWrap, + padding: widget.padding, + itemExtent: widget.itemExtent, + itemCount: widget.itemCount, + itemBuilder: (context, index) { + return AnimatedPadding( + duration: widget.animationDuration, + curve: widget.curve, + padding: _padding, + child: widget.itemBuilder(context, index), + ); + }, + addAutomaticKeepAlives: widget.addAutomaticKeepAlives, + addRepaintBoundaries: widget.addRepaintBoundaries, + addSemanticIndexes: widget.addSemanticIndexes, + cacheExtent: widget.cacheExtent, + semanticChildCount: widget.semanticChildCount, + dragStartBehavior: widget.dragStartBehavior, + keyboardDismissBehavior: widget.keyboardDismissBehavior, + restorationId: widget.restorationId, + clipBehavior: widget.clipBehavior, + ), + ), + ); + } + + /// Handles scroll events to provide elastic overscroll behavior. + /// + /// This method is responsible for determining if the scroll position is at its + /// limits (maximum or minimum) and resetting the elasticity if so. If the scroll + /// is within the valid range, it calculates overscroll using the + /// [ElasticListViewUtils.calculateOverscroll] method and creates a simulation + /// for elastic behavior. The [_controller] is then animated with the calculated + /// simulation, and the elasticity is updated using the [_updateElasticity] method. + void _handleScroll() { + // Given the variability in scroll event handling across devices, this condition + // ensures that we correctly identify when the user has reached the list's boundaries. + // This applies regardless of whether the user is scrolling by dragging, swiping, + // using the mouse wheel, or manipulating the scrollbars. + if (!_scrollController.position.outOfRange && + (_scrollController.offset >= + _scrollController.position.maxScrollExtent || + _scrollController.offset <= + _scrollController.position.minScrollExtent)) { + // Reset elasticity if at scroll limits + _resetElasticity(); + } else { + // Calculate overscroll and simulate elastic behavior + final overscroll = + ElasticListViewUtils.calculateOverscroll(_scrollController); + final simulation = ElasticListViewUtils.calculateSimulation(overscroll); + + // Animate the controller with the calculated simulation + _controller.animateWith(simulation); + + // Update the elasticity based on scroll speed + _updateElasticity(); + } + } + + /// Handles user scroll notifications and resets elasticity on scroll completion. + /// + /// This method is a callback function designed to handle [UserScrollNotification] + /// instances, specifically checking for an idle scroll direction. When the scroll + /// direction becomes idle and there is no ongoing scroll activity, it triggers + /// the `_resetElasticity` method to reset the elasticity of the [ElasticListView]. + /// + /// It utilizes the [SchedulerBinding] to schedule a callback for the next frame + /// to ensure that the reset occurs after the current frame is complete. + bool _notificationHandler(UserScrollNotification notification) { + if (notification.direction == ScrollDirection.idle && + _scrollController.position.isScrollingNotifier is! IdleScrollActivity) { + // Schedule a callback to reset elasticity after the current frame + SchedulerBinding.instance.addPostFrameCallback((_) => _resetElasticity()); + } + // Continue propagating notifications + return false; + } + + /// Resets the elasticity of the [ElasticListView] to its default state. + /// + /// Call this method when you want to reset the elastic effect, such as on + /// user interactions that should clear any ongoing elastic behavior. + void _resetElasticity() { + setState(() { + // Set the controller's value to 0.0 to reset elasticity + _controller.value = 0.0; + + // Reset the elasticity to its default value (1.0) + _elasticity = 1.0; + }); + } + + /// Updates the elasticity of the [ElasticListView] based on the scrolling speed. + /// + /// This method calculates the scroll speed and updates the elasticity value + /// for creating the elastic effect during scrolling. It takes into account + /// the time elapsed since the last update to calculate the scroll speed. + /// + /// If the time delta between updates is zero, the method avoids division by + /// zero to prevent potential issues. The calculated elasticity is then set, + /// and the widget is updated to reflect the changes. + /// + /// This method should be called whenever there is a change in the scrolling + /// behavior to ensure the elasticity is accurately represented in the UI. + void _updateElasticity() { + // Get the current timestamp + final DateTime now = DateTime.now(); + + // Calculate the time difference between the current and previous timestamps + final Duration timeDelta = now.difference(_previousTimestamp); + + // Check for zero timeDelta to avoid division by zero + if (timeDelta.inMilliseconds != 0) { + // Calculate the scroll speed based on controller, offset, and timestamp + final double scrollSpeed = ElasticListViewUtils.calculateScrollSpeed( + _scrollController, + _previousOffset, + _previousTimestamp, + ); + + // Update the previous offset and timestamp + _previousOffset = _scrollController.offset; + _previousTimestamp = now; + + // Calculate the elasticity based on the controller and scroll speed + _elasticity = + ElasticListViewUtils.calculateElasticity(_controller, scrollSpeed); + + // Trigger a widget update to reflect the changes in elasticity + setState(() {}); + } + } +} diff --git a/lib/utils/elastic_list_view_utils.dart b/lib/src/utils/elastic_list_view_utils.dart similarity index 100% rename from lib/utils/elastic_list_view_utils.dart rename to lib/src/utils/elastic_list_view_utils.dart diff --git a/lib/ui/elastic_list_view.dart b/lib/ui/elastic_list_view.dart deleted file mode 100644 index 7143c1c..0000000 --- a/lib/ui/elastic_list_view.dart +++ /dev/null @@ -1,333 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/scheduler.dart'; -import 'package:flutter_elastic_list_view/config/drag_scroll_behavior_configuration.dart'; -import 'package:flutter_elastic_list_view/utils/elastic_list_view_utils.dart'; - -/// A customizable ListView with an elastic effect. -/// -/// This widget provides an extended ListView with additional features such as -/// elastic scrolling, customizable animation curve, and duration. -/// -/// The [ElasticListView] allows you to customize various aspects, including -/// scroll direction, controller, item extent, itemBuilder, and more. -/// -/// Example usage: -/// ```dart -/// ElasticListView( -/// itemCount: 10, -/// itemBuilder: (context, index) { -/// return ListTile( -/// title: Text('Item $index'), -/// ); -/// }, -/// ) -/// ``` -class ElasticListView extends StatefulWidget { - const ElasticListView({ - super.key, - this.scrollDirection = Axis.vertical, - this.reverse = false, - this.controller, - this.primary, - this.physics, - this.shrinkWrap = false, - this.padding, - this.itemExtent, - this.itemExtentBuilder, - this.prototypeItem, - required this.itemBuilder, - this.findChildIndexCallback, - this.itemCount, - this.addAutomaticKeepAlives = true, - this.addRepaintBoundaries = true, - this.addSemanticIndexes = true, - this.cacheExtent, - this.semanticChildCount, - this.dragStartBehavior = DragStartBehavior.start, - this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual, - this.restorationId, - this.clipBehavior = Clip.hardEdge, - this.curve = Curves.easeOut, - this.animationDuration = const Duration(milliseconds: 200), - this.enableDragScrolling = true, - this.elasticityFactor = 4, - }) : assert(elasticityFactor >= 0, - 'Elasticity factor must be greater than or equal to 0.'), - assert(animationDuration >= const Duration(milliseconds: 100), - 'Animation duration should be greater than or equal to 100 ms for optimal user experience.'); - - /// The axis along which the [ElasticListView] scrolls. - final Axis scrollDirection; - - /// Whether the [ElasticListView] scrolls in the reverse direction. - final bool reverse; - - /// An optional controller for the [ElasticListView]. - final ScrollController? controller; - - /// Whether this [ElasticListView] is the primary scroll view associated with the parent. - final bool? primary; - - /// The physics of the [ElasticListView]. - final ScrollPhysics? physics; - - /// Whether the [ElasticListView] should wrap its content or not. - final bool shrinkWrap; - - /// The padding for the [ElasticListView]. - final EdgeInsetsGeometry? padding; - - /// The size of each item in the [ElasticListView]. - final double? itemExtent; - - /// A builder that returns the size of each item in the [ElasticListView]. - final ItemExtentBuilder? itemExtentBuilder; - - /// A prototype of the item that is used for measuring its size. - final Widget? prototypeItem; - - /// Called to build children for the [ElasticListView]. - final IndexedWidgetBuilder itemBuilder; - - /// Callback to find the index of a child based on its key. - final ChildIndexGetter? findChildIndexCallback; - - /// The number of children that the [ElasticListView] should contain. - final int? itemCount; - - /// Whether to add automatic keep-alive widgets to the children of the [ElasticListView]. - final bool addAutomaticKeepAlives; - - /// Whether to add repaint boundaries to the children of the [ElasticListView]. - final bool addRepaintBoundaries; - - /// Whether to add semantic indexes to the children of the [ElasticListView]. - final bool addSemanticIndexes; - - /// The extent of the cache in pixels. - final double? cacheExtent; - - /// The number of children that are taken into account for semantic indexing. - final int? semanticChildCount; - - /// The drag start behavior for the [ElasticListView]. - final DragStartBehavior dragStartBehavior; - - /// The keyboard dismissal behavior for the [ElasticListView]. - final ScrollViewKeyboardDismissBehavior keyboardDismissBehavior; - - /// An identifier to use for state restoration. - final String? restorationId; - - /// The clip behavior for the [ElasticListView]. - final Clip clipBehavior; - - /// The curve for the elastic scrolling animation. - final Curve curve; - - /// The duration of the elastic scrolling animation. - final Duration animationDuration; - - /// Enables drag scrolling behavior for the [ElasticListView]. - final bool enableDragScrolling; - - /// The elasticity factor for the [ElasticListView]. - final int elasticityFactor; - - @override - ElasticListViewState createState() => ElasticListViewState(); -} - -class ElasticListViewState extends State - with SingleTickerProviderStateMixin { - /// Animation controller for managing elastic overscroll animation. - late AnimationController _controller; - - /// Scroll controller for the underlying list view. - late ScrollController _scrollController; - - /// Current elasticity factor for overscroll behavior. - double _elasticity = 1.0; - - /// Previous scroll offset to calculate scroll speed. - double _previousOffset = 0.0; - - /// Timestamp of the previous scroll update. - DateTime _previousTimestamp = DateTime.now(); - - @override - void initState() { - super.initState(); - - _controller = AnimationController( - vsync: this, - duration: widget.animationDuration, - ); - - _scrollController = widget.controller ?? ScrollController(); - _scrollController.addListener(_handleScroll); - } - - @override - void dispose() { - // Dispose of the animation and scroll controllers to release resources. - _controller.dispose(); - _scrollController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final padding = widget.scrollDirection == Axis.vertical - ? EdgeInsets.symmetric( - vertical: (_elasticity - 1) * widget.elasticityFactor) - : EdgeInsets.symmetric( - horizontal: (_elasticity - 1) * widget.elasticityFactor); - return ScrollConfiguration( - behavior: widget.enableDragScrolling - ? DragScrollBehavior() - : const ScrollBehavior(), - child: NotificationListener( - onNotification: _notificationHandler, - child: ListView.builder( - scrollDirection: widget.scrollDirection, - reverse: widget.reverse, - controller: _scrollController, - primary: widget.primary, - physics: widget.physics, - shrinkWrap: widget.shrinkWrap, - padding: widget.padding, - itemExtent: widget.itemExtent, - itemCount: widget.itemCount, - itemBuilder: (context, index) { - return AnimatedPadding( - duration: widget.animationDuration, - curve: widget.curve, - padding: padding, - child: widget.itemBuilder(context, index), - ); - }, - addAutomaticKeepAlives: widget.addAutomaticKeepAlives, - addRepaintBoundaries: widget.addRepaintBoundaries, - addSemanticIndexes: widget.addSemanticIndexes, - cacheExtent: widget.cacheExtent, - semanticChildCount: widget.semanticChildCount, - dragStartBehavior: widget.dragStartBehavior, - keyboardDismissBehavior: widget.keyboardDismissBehavior, - restorationId: widget.restorationId, - clipBehavior: widget.clipBehavior, - ), - ), - ); - } - - /// Handles scroll events to provide elastic overscroll behavior. - /// - /// This method is responsible for determining if the scroll position is at its - /// limits (maximum or minimum) and resetting the elasticity if so. If the scroll - /// is within the valid range, it calculates overscroll using the - /// [ElasticListViewUtils.calculateOverscroll] method and creates a simulation - /// for elastic behavior. The [_controller] is then animated with the calculated - /// simulation, and the elasticity is updated using the [_updateElasticity] method. - void _handleScroll() { - // Given the variability in scroll event handling across devices, this condition - // ensures that we correctly identify when the user has reached the list's boundaries. - // This applies regardless of whether the user is scrolling by dragging, swiping, - // using the mouse wheel, or manipulating the scrollbars. - if (!_scrollController.position.outOfRange && - (_scrollController.offset >= - _scrollController.position.maxScrollExtent || - _scrollController.offset <= - _scrollController.position.minScrollExtent)) { - // Reset elasticity if at scroll limits - _resetElasticity(); - } else { - // Calculate overscroll and simulate elastic behavior - final overscroll = - ElasticListViewUtils.calculateOverscroll(_scrollController); - final simulation = ElasticListViewUtils.calculateSimulation(overscroll); - - // Animate the controller with the calculated simulation - _controller.animateWith(simulation); - - // Update the elasticity based on scroll speed - _updateElasticity(); - } - } - - /// Handles user scroll notifications and resets elasticity on scroll completion. - /// - /// This method is a callback function designed to handle [UserScrollNotification] - /// instances, specifically checking for an idle scroll direction. When the scroll - /// direction becomes idle and there is no ongoing scroll activity, it triggers - /// the `_resetElasticity` method to reset the elasticity of the [ElasticListView]. - /// - /// It utilizes the [SchedulerBinding] to schedule a callback for the next frame - /// to ensure that the reset occurs after the current frame is complete. - bool _notificationHandler(UserScrollNotification notification) { - if (notification.direction == ScrollDirection.idle && - _scrollController.position.isScrollingNotifier is! IdleScrollActivity) { - // Schedule a callback to reset elasticity after the current frame - SchedulerBinding.instance.addPostFrameCallback((_) => _resetElasticity()); - } - // Continue propagating notifications - return false; - } - - /// Resets the elasticity of the [ElasticListView] to its default state. - /// - /// Call this method when you want to reset the elastic effect, such as on - /// user interactions that should clear any ongoing elastic behavior. - void _resetElasticity() { - setState(() { - // Set the controller's value to 0.0 to reset elasticity - _controller.value = 0.0; - - // Reset the elasticity to its default value (1.0) - _elasticity = 1.0; - }); - } - - /// Updates the elasticity of the [ElasticListView] based on the scrolling speed. - /// - /// This method calculates the scroll speed and updates the elasticity value - /// for creating the elastic effect during scrolling. It takes into account - /// the time elapsed since the last update to calculate the scroll speed. - /// - /// If the time delta between updates is zero, the method avoids division by - /// zero to prevent potential issues. The calculated elasticity is then set, - /// and the widget is updated to reflect the changes. - /// - /// This method should be called whenever there is a change in the scrolling - /// behavior to ensure the elasticity is accurately represented in the UI. - void _updateElasticity() { - // Get the current timestamp - final DateTime now = DateTime.now(); - - // Calculate the time difference between the current and previous timestamps - final Duration timeDelta = now.difference(_previousTimestamp); - - // Check for zero timeDelta to avoid division by zero - if (timeDelta.inMilliseconds != 0) { - // Calculate the scroll speed based on controller, offset, and timestamp - final double scrollSpeed = ElasticListViewUtils.calculateScrollSpeed( - _scrollController, - _previousOffset, - _previousTimestamp, - ); - - // Update the previous offset and timestamp - _previousOffset = _scrollController.offset; - _previousTimestamp = now; - - // Calculate the elasticity based on the controller and scroll speed - _elasticity = - ElasticListViewUtils.calculateElasticity(_controller, scrollSpeed); - - // Trigger a widget update to reflect the changes in elasticity - setState(() {}); - } - } -} From c1c54560919e981aa32076d012cd662d4a1de345 Mon Sep 17 00:00:00 2001 From: Daniel Coyula Date: Tue, 23 Jan 2024 13:40:01 -0500 Subject: [PATCH 4/8] Add tests for the package --- test/elastic_list_view_test.dart | 127 +++++++++++++++++++++++++ test/elastic_list_view_utils_test.dart | 113 ++++++++++++++++++++++ test/widget_test.dart | 30 ------ 3 files changed, 240 insertions(+), 30 deletions(-) create mode 100644 test/elastic_list_view_test.dart create mode 100644 test/elastic_list_view_utils_test.dart delete mode 100644 test/widget_test.dart diff --git a/test/elastic_list_view_test.dart b/test/elastic_list_view_test.dart new file mode 100644 index 0000000..8a04e05 --- /dev/null +++ b/test/elastic_list_view_test.dart @@ -0,0 +1,127 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_elastic_list_view/elastic_list_view.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// The main entry point for running widget tests. +void main() { + late Widget widget; + + setUp(() { + widget = MaterialApp( + home: ElasticListView.builder( + itemCount: 50, + itemBuilder: (BuildContext context, int index) => Material( + child: ListTile( + title: Text('Item $index'), + ), + ), + ), + ); + }); + // Testing group for [ElasticListView]. + group('ElasticListView', () { + // Test case: Ensure that ElasticListView renders children correctly. + testWidgets('renders children correctly', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: ElasticListView( + children: [ + Container( + height: 100, + color: Colors.red, + ), + Container( + height: 100, + color: Colors.blue, + ), + Container( + height: 100, + color: Colors.green, + ), + ], + ), + ), + ), + ); + + // Expecting to find three Container widgets and one ElasticListView widget. + expect(find.byType(Container), findsNWidgets(3)); + expect(find.byType(ElasticListView), findsOneWidget); + }); + + // Test case: Ensure that ElasticListView.builder renders children correctly. + testWidgets('builder renders children correctly', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: ElasticListView.builder( + itemCount: 3, + itemBuilder: (context, index) { + return Container( + height: 100, + color: Colors.red, + ); + }, + ), + ), + ), + ); + + // Expecting to find three Container widgets and one ElasticListView widget. + expect(find.byType(Container), findsNWidgets(3)); + expect(find.byType(ElasticListView), findsOneWidget); + }); + + // Test case: Animate scroll and verify elasticity. + testWidgets('Animate scroll, verify elasticity', + (WidgetTester tester) async { + await tester.pumpWidget(widget); + + final itemFinder = find.byType(ElasticListView); + + final state = tester.state(itemFinder) as ElasticListViewState; + + // Initial elasticity should be 1.0. + expect(state.elasticity, equals(1.0)); + + // Simulating a scroll gesture. + final gesture = await tester.startGesture(const Offset(0, 50)); + await gesture.moveBy(const Offset(0, -50)); + await tester.pump(); + + // Elasticity should be greater than 1.0 during scrolling. + expect(state.elasticity, greaterThan(1.0)); + + // Releasing the scroll gesture and waiting for settling. + await gesture.up(); + await tester.pumpAndSettle(); + + // Elasticity should return to 1.0 after settling. + expect(state.elasticity, equals(1.0)); + }); + + // Test case: Verify elasticity remains 1.0 when scrolling at the start. + testWidgets('Verify elasticity remains 1.0 when scrolling at start', + (WidgetTester tester) async { + await tester.pumpWidget(widget); + + final itemFinder = find.byType(ElasticListView); + + // Getting the state of the ElasticListView. + final state = tester.state(itemFinder) as ElasticListViewState; + + // Initial elasticity should be 1.0. + expect(state.elasticity, equals(1.0)); + + // Simulating a scroll gesture at the start. + final gesture = await tester.startGesture(const Offset(0, 50)); + await gesture.moveBy(const Offset(0, 50)); + await tester.pump(); + + // Elasticity should remain 1.0 when scrolling at the start. + expect(state.elasticity, equals(1.0)); + }); + }); +} diff --git a/test/elastic_list_view_utils_test.dart b/test/elastic_list_view_utils_test.dart new file mode 100644 index 0000000..b8e4b6e --- /dev/null +++ b/test/elastic_list_view_utils_test.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/physics.dart'; +import 'package:flutter_elastic_list_view/elastic_list_view.dart'; +import 'package:flutter_elastic_list_view/src/utils/elastic_list_view_utils.dart'; +import 'package:flutter_test/flutter_test.dart'; + +/// The main entry point for running tests. +void main() { + // Testing group for [ElasticListView]. + group('ElasticListView', () { + // Test case: Ensure an AssertionError is thrown if elasticityFactor is zero or negative. + test('throws AssertionError if elasticityFactor is zero or negative', () { + expect( + () => ElasticListView(elasticityFactor: -10, children: const []), + throwsAssertionError, + ); + }); + + // Test case: Ensure an AssertionError is thrown if animationDuration is zero or negative. + test('throws AssertionError if animationDuration is zero or negative', () { + expect( + () => ElasticListView( + animationDuration: Duration.zero, children: const []), + throwsAssertionError); + + expect( + () => ElasticListView( + animationDuration: const Duration(milliseconds: -100), + children: const []), + throwsAssertionError); + }); + + // Test case: Ensure default values are used when parameters are not provided. + test('uses default values when parameters are not provided', () { + const defaultElasticityFactor = 4; + const defaultAnimationDuration = Duration(milliseconds: 200); + final listView = ElasticListView(children: const []); + + expect(listView.elasticityFactor, equals(defaultElasticityFactor)); + expect(listView.animationDuration, equals(defaultAnimationDuration)); + }); + }); + + // Testing group for [ElasticListViewUtils]. + group('ElasticListViewUtils', () { + // Test case: Ensure elasticity is calculated based on controller value and scroll speed. + test('calculates elasticity based on controller value and scroll speed', + () { + final controller = AnimationController(vsync: const TestVSync()); + + final minElasticity = + ElasticListViewUtils.calculateElasticity(controller, 0); + expect(minElasticity, 1.0); + + final elasticity1 = + ElasticListViewUtils.calculateElasticity(controller, 1); + + controller.value = 0.5; + + final elasticity2 = + ElasticListViewUtils.calculateElasticity(controller, 1.5); + expect(elasticity2, greaterThan(elasticity1)); + + final elasticity3 = + ElasticListViewUtils.calculateElasticity(controller, 2); + expect(elasticity3, greaterThan(elasticity2)); + }); + + // Test case: Ensure calculateSimulation returns a valid SpringSimulation. + test('calculateSimulation returns a valid SpringSimulation', () { + const overscroll = 100.0; + final simulation = ElasticListViewUtils.calculateSimulation(overscroll); + expect(simulation, isA()); + }); + + // Test case: Ensure calculateElasticity returns a valid elasticity value. + test('calculateElasticity returns a valid elasticity value', () { + final controller = AnimationController(vsync: const TestVSync()); + const scrollSpeed = 2.0; + final elasticity = + ElasticListViewUtils.calculateElasticity(controller, scrollSpeed); + expect( + elasticity, + inInclusiveRange(ElasticListViewUtils.minElasticity, + ElasticListViewUtils.maxElasticity)); + }); + + // Test case: Ensure calculateElasticity returns correct value for various inputs. + test('calculateElasticity returns correct value for various inputs', () { + final controller = AnimationController(vsync: const TestVSync()); + + controller.value = 0; + var elasticity = ElasticListViewUtils.calculateElasticity(controller, 0); + + expect(elasticity, equals(1.0)); + + controller.value = 0.5; + elasticity = ElasticListViewUtils.calculateElasticity(controller, 0); + + expect(elasticity, equals(1.1)); + + controller.value = 0; + elasticity = ElasticListViewUtils.calculateElasticity(controller, 2); + + expect(elasticity, equals(3.0)); + + controller.value = 0.5; + elasticity = ElasticListViewUtils.calculateElasticity(controller, 2); + + expect(elasticity, equals(3.0)); + }); + }); +} diff --git a/test/widget_test.dart b/test/widget_test.dart deleted file mode 100644 index 27474b1..0000000 --- a/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:flutter_elastic_list_view/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} From a4aa200aa869df0130b4d40b269859a9c702c87d Mon Sep 17 00:00:00 2001 From: Daniel Coyula Date: Tue, 23 Jan 2024 13:41:58 -0500 Subject: [PATCH 5/8] Include example app for package usage --- example/.gitignore | 43 ++++ example/.metadata | 45 ++++ example/README.md | 16 ++ example/analysis_options.yaml | 28 +++ {android => example/android}/.gitignore | 0 {android => example/android}/app/build.gradle | 4 +- .../app/src/debug/AndroidManifest.xml | 0 .../android}/app/src/main/AndroidManifest.xml | 2 +- .../dev/dctech/example}/MainActivity.kt | 2 +- .../res/drawable-v21/launch_background.xml | 0 .../main/res/drawable/launch_background.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../app/src/main/res/values-night/styles.xml | 0 .../app/src/main/res/values/styles.xml | 0 .../app/src/profile/AndroidManifest.xml | 0 {android => example/android}/build.gradle | 0 .../android}/gradle.properties | 0 .../gradle/wrapper/gradle-wrapper.properties | 0 {android => example/android}/settings.gradle | 0 {ios => example/ios}/.gitignore | 0 .../ios}/Flutter/AppFrameworkInfo.plist | 0 example/ios/Flutter/Debug.xcconfig | 2 + example/ios/Flutter/Release.xcconfig | 2 + example/ios/Podfile | 44 ++++ example/ios/Podfile.lock | 16 ++ .../ios}/Runner.xcodeproj/project.pbxproj | 133 ++++++++++-- .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 0 .../xcshareddata/xcschemes/Runner.xcscheme | 0 .../contents.xcworkspacedata | 3 + .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcshareddata/WorkspaceSettings.xcsettings | 0 {ios => example/ios}/Runner/AppDelegate.swift | 0 .../AppIcon.appiconset/Contents.json | 0 .../Icon-App-1024x1024@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin .../Icon-App-83.5x83.5@2x.png | Bin .../LaunchImage.imageset/Contents.json | 0 .../LaunchImage.imageset/LaunchImage.png | Bin .../LaunchImage.imageset/LaunchImage@2x.png | Bin .../LaunchImage.imageset/LaunchImage@3x.png | Bin .../LaunchImage.imageset/README.md | 0 .../Runner/Base.lproj/LaunchScreen.storyboard | 0 .../ios}/Runner/Base.lproj/Main.storyboard | 0 {ios => example/ios}/Runner/Info.plist | 4 +- .../ios}/Runner/Runner-Bridging-Header.h | 0 .../ios}/RunnerTests/RunnerTests.swift | 0 .../elastic_list_view_builder_example.dart | 45 ++++ example/lib/elastic_list_view_example.dart | 30 +++ .../elastic_list_view_separated_example.dart | 29 +++ example/lib/main.dart | 77 +++++++ example/pubspec.lock | 195 ++++++++++++++++++ example/pubspec.yaml | 28 +++ {web => example/web}/favicon.png | Bin {web => example/web}/icons/Icon-192.png | Bin {web => example/web}/icons/Icon-512.png | Bin .../web}/icons/Icon-maskable-192.png | Bin .../web}/icons/Icon-maskable-512.png | Bin {web => example/web}/index.html | 4 +- {web => example/web}/manifest.json | 4 +- ios/Flutter/Debug.xcconfig | 1 - ios/Flutter/Release.xcconfig | 1 - lib/main.dart | 39 ---- 80 files changed, 724 insertions(+), 73 deletions(-) create mode 100644 example/.gitignore create mode 100644 example/.metadata create mode 100644 example/README.md create mode 100644 example/analysis_options.yaml rename {android => example/android}/.gitignore (100%) rename {android => example/android}/app/build.gradle (94%) rename {android => example/android}/app/src/debug/AndroidManifest.xml (100%) rename {android => example/android}/app/src/main/AndroidManifest.xml (96%) rename {android/app/src/main/kotlin/com/example/flutter_elastic_list_view => example/android/app/src/main/kotlin/dev/dctech/example}/MainActivity.kt (67%) rename {android => example/android}/app/src/main/res/drawable-v21/launch_background.xml (100%) rename {android => example/android}/app/src/main/res/drawable/launch_background.xml (100%) rename {android => example/android}/app/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename {android => example/android}/app/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename {android => example/android}/app/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename {android => example/android}/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {android => example/android}/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {android => example/android}/app/src/main/res/values-night/styles.xml (100%) rename {android => example/android}/app/src/main/res/values/styles.xml (100%) rename {android => example/android}/app/src/profile/AndroidManifest.xml (100%) rename {android => example/android}/build.gradle (100%) rename {android => example/android}/gradle.properties (100%) rename {android => example/android}/gradle/wrapper/gradle-wrapper.properties (100%) rename {android => example/android}/settings.gradle (100%) rename {ios => example/ios}/.gitignore (100%) rename {ios => example/ios}/Flutter/AppFrameworkInfo.plist (100%) create mode 100644 example/ios/Flutter/Debug.xcconfig create mode 100644 example/ios/Flutter/Release.xcconfig create mode 100644 example/ios/Podfile create mode 100644 example/ios/Podfile.lock rename {ios => example/ios}/Runner.xcodeproj/project.pbxproj (77%) rename {ios => example/ios}/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata (100%) rename {ios => example/ios}/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {ios => example/ios}/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (100%) rename {ios => example/ios}/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme (100%) rename {ios => example/ios}/Runner.xcworkspace/contents.xcworkspacedata (67%) rename {ios => example/ios}/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {ios => example/ios}/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings (100%) rename {ios => example/ios}/Runner/AppDelegate.swift (100%) rename {ios => example/ios}/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {ios => example/ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json (100%) rename {ios => example/ios}/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png (100%) rename {ios => example/ios}/Runner/Assets.xcassets/LaunchImage.imageset/README.md (100%) rename {ios => example/ios}/Runner/Base.lproj/LaunchScreen.storyboard (100%) rename {ios => example/ios}/Runner/Base.lproj/Main.storyboard (100%) rename {ios => example/ios}/Runner/Info.plist (94%) rename {ios => example/ios}/Runner/Runner-Bridging-Header.h (100%) rename {ios => example/ios}/RunnerTests/RunnerTests.swift (100%) create mode 100644 example/lib/elastic_list_view_builder_example.dart create mode 100644 example/lib/elastic_list_view_example.dart create mode 100644 example/lib/elastic_list_view_separated_example.dart create mode 100644 example/lib/main.dart create mode 100644 example/pubspec.lock create mode 100644 example/pubspec.yaml rename {web => example/web}/favicon.png (100%) rename {web => example/web}/icons/Icon-192.png (100%) rename {web => example/web}/icons/Icon-512.png (100%) rename {web => example/web}/icons/Icon-maskable-192.png (100%) rename {web => example/web}/icons/Icon-maskable-512.png (100%) rename {web => example/web}/index.html (93%) rename {web => example/web}/manifest.json (90%) delete mode 100644 ios/Flutter/Debug.xcconfig delete mode 100644 ios/Flutter/Release.xcconfig delete mode 100644 lib/main.dart diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 0000000..29a3a50 --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,43 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# 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/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/example/.metadata b/example/.metadata new file mode 100644 index 0000000..3e6e02a --- /dev/null +++ b/example/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + - platform: android + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + - platform: ios + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + - platform: linux + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + - platform: macos + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + - platform: web + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + - platform: windows + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/example/README.md b/example/README.md new file mode 100644 index 0000000..2b3fce4 --- /dev/null +++ b/example/README.md @@ -0,0 +1,16 @@ +# example + +A new Flutter project. + +## Getting Started + +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://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/example/analysis_options.yaml @@ -0,0 +1,28 @@ +# 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.dev/lints. + # + # 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/android/.gitignore b/example/android/.gitignore similarity index 100% rename from android/.gitignore rename to example/android/.gitignore diff --git a/android/app/build.gradle b/example/android/app/build.gradle similarity index 94% rename from android/app/build.gradle rename to example/android/app/build.gradle index 3aefd89..e68d950 100644 --- a/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -23,7 +23,7 @@ if (flutterVersionName == null) { } android { - namespace "com.example.flutter_elastic_list_view" + namespace "dev.dctech.example" compileSdkVersion flutter.compileSdkVersion ndkVersion flutter.ndkVersion @@ -42,7 +42,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.flutter_elastic_list_view" + applicationId "dev.dctech.example" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdkVersion flutter.minSdkVersion diff --git a/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from android/app/src/debug/AndroidManifest.xml rename to example/android/app/src/debug/AndroidManifest.xml diff --git a/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml similarity index 96% rename from android/app/src/main/AndroidManifest.xml rename to example/android/app/src/main/AndroidManifest.xml index 8cbc093..19b862e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + end +end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock new file mode 100644 index 0000000..39d5fb0 --- /dev/null +++ b/example/ios/Podfile.lock @@ -0,0 +1,16 @@ +PODS: + - Flutter (1.0.0) + +DEPENDENCIES: + - Flutter (from `Flutter`) + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + +SPEC CHECKSUMS: + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + +PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189 + +COCOAPODS: 1.14.3 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj similarity index 77% rename from ios/Runner.xcodeproj/project.pbxproj rename to example/ios/Runner.xcodeproj/project.pbxproj index aca1320..592535e 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,12 +8,14 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 1E6746C3AC57378B17D61A80 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7B1F882A825491C6A2DB54D /* Pods_Runner.framework */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 81FCDC2322DCAEFFE87268C7 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CFADF69F476D05F20B98B325 /* Pods_RunnerTests.framework */; }; 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 */; }; - 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -40,9 +42,13 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 1192D41585ABD7BA177FB4B2 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 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 = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 73017D9ED72DE3769A5A3F27 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; 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 = ""; }; @@ -53,30 +59,41 @@ 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 = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; - 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + A7B1F882A825491C6A2DB54D /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + ADA31BC6DD9682F3C775BB51 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + B2D19AC0B249F7DD22616AC4 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + CFADF69F476D05F20B98B325 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F096D38268065BD4B42F4A43 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + F1EBDCD1BA439546DF95A886 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 7264B553B78F3485F8E16035 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 81FCDC2322DCAEFFE87268C7 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 1E6746C3AC57378B17D61A80 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 9740EEB11CF90186004384FC /* Flutter */ = { + 0C3A6908CA18925E11ED94F9 /* Frameworks */ = { isa = PBXGroup; children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, + A7B1F882A825491C6A2DB54D /* Pods_Runner.framework */, + CFADF69F476D05F20B98B325 /* Pods_RunnerTests.framework */, ); - name = Flutter; + name = Frameworks; sourceTree = ""; }; 331C8082294A63A400263BE5 /* RunnerTests */ = { @@ -87,6 +104,17 @@ path = RunnerTests; sourceTree = ""; }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( @@ -94,6 +122,8 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, + C3DF8172020EF66539372383 /* Pods */, + 0C3A6908CA18925E11ED94F9 /* Frameworks */, ); sourceTree = ""; }; @@ -121,6 +151,19 @@ path = Runner; sourceTree = ""; }; + C3DF8172020EF66539372383 /* Pods */ = { + isa = PBXGroup; + children = ( + ADA31BC6DD9682F3C775BB51 /* Pods-Runner.debug.xcconfig */, + 73017D9ED72DE3769A5A3F27 /* Pods-Runner.release.xcconfig */, + F096D38268065BD4B42F4A43 /* Pods-Runner.profile.xcconfig */, + 1192D41585ABD7BA177FB4B2 /* Pods-RunnerTests.debug.xcconfig */, + F1EBDCD1BA439546DF95A886 /* Pods-RunnerTests.release.xcconfig */, + B2D19AC0B249F7DD22616AC4 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -128,9 +171,10 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( + 0FEA4EB1F052BB650C37852F /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, - 331C807E294A63A400263BE5 /* Frameworks */, 331C807F294A63A400263BE5 /* Resources */, + 7264B553B78F3485F8E16035 /* Frameworks */, ); buildRules = ( ); @@ -146,6 +190,7 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 7860D6268A1FD1937DD77EE1 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, @@ -223,6 +268,28 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 0FEA4EB1F052BB650C37852F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -239,6 +306,28 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 7860D6268A1FD1937DD77EE1 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -361,14 +450,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = MQT2MSRHJG; + DEVELOPMENT_TEAM = ; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterElasticListView; + PRODUCT_BUNDLE_IDENTIFIER = dev.dctech.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -378,14 +467,14 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = AE0B7B92F70575B8D7E0D07E /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = 1192D41585ABD7BA177FB4B2 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterElasticListView.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = dev.dctech.example.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -396,14 +485,14 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 89B67EB44CE7B6631473024E /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = F1EBDCD1BA439546DF95A886 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterElasticListView.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = dev.dctech.example.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; @@ -412,14 +501,14 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 640959BDD8F10B91D80A66BE /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = B2D19AC0B249F7DD22616AC4 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterElasticListView.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = dev.dctech.example.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; @@ -540,14 +629,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = MQT2MSRHJG; + DEVELOPMENT_TEAM = ; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterElasticListView; + PRODUCT_BUNDLE_IDENTIFIER = dev.dctech.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -563,14 +652,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = MQT2MSRHJG; + DEVELOPMENT_TEAM = ; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterElasticListView; + PRODUCT_BUNDLE_IDENTIFIER = dev.dctech.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 67% rename from ios/Runner.xcworkspace/contents.xcworkspacedata rename to example/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift similarity index 100% rename from ios/Runner/AppDelegate.swift rename to example/ios/Runner/AppDelegate.swift diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/ios/Runner/Base.lproj/LaunchScreen.storyboard b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from ios/Runner/Base.lproj/LaunchScreen.storyboard rename to example/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/ios/Runner/Base.lproj/Main.storyboard b/example/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from ios/Runner/Base.lproj/Main.storyboard rename to example/ios/Runner/Base.lproj/Main.storyboard diff --git a/ios/Runner/Info.plist b/example/ios/Runner/Info.plist similarity index 94% rename from ios/Runner/Info.plist rename to example/ios/Runner/Info.plist index 5acaf25..5458fc4 100644 --- a/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Flutter Elastic List View + Example CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -13,7 +13,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - flutter_elastic_list_view + example CFBundlePackageType APPL CFBundleShortVersionString diff --git a/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h similarity index 100% rename from ios/Runner/Runner-Bridging-Header.h rename to example/ios/Runner/Runner-Bridging-Header.h diff --git a/ios/RunnerTests/RunnerTests.swift b/example/ios/RunnerTests/RunnerTests.swift similarity index 100% rename from ios/RunnerTests/RunnerTests.swift rename to example/ios/RunnerTests/RunnerTests.swift diff --git a/example/lib/elastic_list_view_builder_example.dart b/example/lib/elastic_list_view_builder_example.dart new file mode 100644 index 0000000..9ad81e3 --- /dev/null +++ b/example/lib/elastic_list_view_builder_example.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_elastic_list_view/elastic_list_view.dart'; + +class ElasticListViewBuilderExample extends StatefulWidget { + const ElasticListViewBuilderExample({super.key}); + + @override + State createState() => + _ElasticListViewBuilderExampleState(); +} + +class _ElasticListViewBuilderExampleState + extends State { + late final ScrollController controller; + + @override + void initState() { + super.initState(); + controller = ScrollController(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('ElasticListView.builder'), + ), + body: ElasticListView.builder( + controller: controller, + elasticityFactor: 8, + itemCount: 50, + itemBuilder: (context, index) { + return Container( + height: 100, + margin: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.green[(index % 9 + 1) * 100], + borderRadius: BorderRadius.circular(16), + ), + ); + }, + ), + ); + } +} diff --git a/example/lib/elastic_list_view_example.dart b/example/lib/elastic_list_view_example.dart new file mode 100644 index 0000000..9053b57 --- /dev/null +++ b/example/lib/elastic_list_view_example.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_elastic_list_view/elastic_list_view.dart'; + +class ElasticListViewExample extends StatelessWidget { + const ElasticListViewExample({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('ElasticListView'), + ), + body: ElasticListView( + children: List.generate( + 50, + (index) { + return Container( + height: 100, + margin: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.red[(index % 9 + 1) * 100], + borderRadius: BorderRadius.circular(16), + ), + ); + }, + ), + ), + ); + } +} diff --git a/example/lib/elastic_list_view_separated_example.dart b/example/lib/elastic_list_view_separated_example.dart new file mode 100644 index 0000000..a2e2df7 --- /dev/null +++ b/example/lib/elastic_list_view_separated_example.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_elastic_list_view/elastic_list_view.dart'; + +class ElasticListViewSeparatedExample extends StatelessWidget { + const ElasticListViewSeparatedExample({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('ElasticListView.separated'), + ), + body: ElasticListView.separated( + itemCount: 50, + separatorBuilder: (context, index) => const Divider(), + itemBuilder: (context, index) { + return Container( + height: 100, + margin: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.blue[(index % 9 + 1) * 100], + borderRadius: BorderRadius.circular(16), + ), + ); + }, + ), + ); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart new file mode 100644 index 0000000..296fb25 --- /dev/null +++ b/example/lib/main.dart @@ -0,0 +1,77 @@ +import 'package:example/elastic_list_view_builder_example.dart'; +import 'package:example/elastic_list_view_example.dart'; +import 'package:example/elastic_list_view_separated_example.dart'; +import 'package:flutter/material.dart'; + +void main() => runApp(const MyApp()); + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter ElasticListView Demo', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), + ), + home: Scaffold( + appBar: AppBar( + title: const Text('Flutter ElasticListView Demo'), + ), + body: Builder( + builder: (context) => Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 450), + child: ListView( + padding: const EdgeInsets.all(8), + children: const [ + _CustomButton( + text: 'ElasticListView', + page: ElasticListViewExample(), + ), + _CustomButton( + text: 'ElasticListView.separated', + page: ElasticListViewSeparatedExample(), + ), + _CustomButton( + text: 'ElasticListView.builder', + page: ElasticListViewBuilderExample(), + ), + ], + ), + ), + ), + ), + ), + ); + } +} + +class _CustomButton extends StatelessWidget { + const _CustomButton({ + required this.text, + required this.page, + }); + + final String text; + final Widget page; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4.0), + child: ElevatedButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (_) => page, + ), + ); + }, + child: Text(text), + ), + ); + } +} diff --git a/example/pubspec.lock b/example/pubspec.lock new file mode 100644 index 0000000..2ed50f5 --- /dev/null +++ b/example/pubspec.lock @@ -0,0 +1,195 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + url: "https://pub.dev" + source: hosted + version: "1.0.6" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_elastic_list_view: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.0.1" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + url: "https://pub.dev" + source: hosted + version: "2.0.3" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + lints: + dependency: transitive + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + url: "https://pub.dev" + source: hosted + version: "0.12.16" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + meta: + dependency: transitive + description: + name: meta + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + url: "https://pub.dev" + source: hosted + version: "1.10.0" + path: + dependency: transitive + description: + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" + source: hosted + version: "1.8.3" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + url: "https://pub.dev" + source: hosted + version: "0.6.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + url: "https://pub.dev" + source: hosted + version: "0.3.0" +sdks: + dart: ">=3.2.3 <4.0.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml new file mode 100644 index 0000000..b1cc9a9 --- /dev/null +++ b/example/pubspec.yaml @@ -0,0 +1,28 @@ +name: example +description: "A new Flutter project." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +version: 1.0.0+1 + +environment: + sdk: '>=3.2.3 <4.0.0' + +dependencies: + flutter: + sdk: flutter + + cupertino_icons: ^1.0.2 + flutter_elastic_list_view: + path: ../ + +dev_dependencies: + flutter_test: + sdk: flutter + + flutter_lints: ^2.0.0 + +flutter: + + uses-material-design: true diff --git a/web/favicon.png b/example/web/favicon.png similarity index 100% rename from web/favicon.png rename to example/web/favicon.png diff --git a/web/icons/Icon-192.png b/example/web/icons/Icon-192.png similarity index 100% rename from web/icons/Icon-192.png rename to example/web/icons/Icon-192.png diff --git a/web/icons/Icon-512.png b/example/web/icons/Icon-512.png similarity index 100% rename from web/icons/Icon-512.png rename to example/web/icons/Icon-512.png diff --git a/web/icons/Icon-maskable-192.png b/example/web/icons/Icon-maskable-192.png similarity index 100% rename from web/icons/Icon-maskable-192.png rename to example/web/icons/Icon-maskable-192.png diff --git a/web/icons/Icon-maskable-512.png b/example/web/icons/Icon-maskable-512.png similarity index 100% rename from web/icons/Icon-maskable-512.png rename to example/web/icons/Icon-maskable-512.png diff --git a/web/index.html b/example/web/index.html similarity index 93% rename from web/index.html rename to example/web/index.html index 68153a9..45cf2ca 100644 --- a/web/index.html +++ b/example/web/index.html @@ -23,13 +23,13 @@ - + - flutter_elastic_list_view + example