Commit d4928c02 authored by 汪林玲's avatar 汪林玲

重新封装waterfall_flow

parents
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.packages
.pub-cache/
.pub/
build/
# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Flutter.podspec
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/Flutter/flutter_export_environment.sh
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
.packages
pubspec.lock
\ No newline at end of file
# 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: ee032f67c734e607d8ea5c870ba744daf4bf56e7
channel: master
project_type: package
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "debug",
"request": "launch",
"type": "dart",
"program": "example/lib/main.dart",
}
]
}
\ No newline at end of file
{
"dart.flutterSdkPath": "/Users/roott/Documents/Tools/flutter/master",
"spellright.language": [
"en"
],
"spellright.documentTypes": [
"markdown",
"latex",
"plaintext",
"dart"
],
"spellright.parserByClass": {
"dart": {
"parser": "code"
}
}
}
\ No newline at end of file
## 2.0.5
* getter 'minTrailingIndex' was called on null(#18)
## 2.0.4
* keep the trailingChildren around when run out of children before reaching the scroll offset(#14)
## 2.0.3
* fix closeToTrailing issue. extended_list#6
## 2.0.2
* fix wrong layout when child's size is changed.
## 2.0.1
* fix wrong layout when itemCount is changed.(#6)
## 2.0.0
* breaking change: fix typo(fullCrossAxisExtend => fullCrossAxisExtent)
* breaking change: SliverWaterfallFlowDelegate is replace with SliverWaterfallFlowDelegateWithFixedCrossAxisCount and SliverWaterfallFlowDelegateWithMaxCrossAxisExtent.
* support WaterfallFlow.count, WaterfallFlow.extend,SliverWaterfallFlow.count,SliverWaterfallFlow.extend.
## 1.0.1
* add miss dragStartBehavior.
## 1.0.0
* fix sdk error.
* support ScrollViewKeyboardDismissBehavior(breaking change 1.17.0)
## 0.1.5
* fix sdk error.
## 0.1.4
* fix leading items are missing when pull to refresh.
* fix analysis_options
## 0.1.3
* web support.
## 0.1.2
* update demo.
## 0.1.1
* update description.
# 0.1.0
* first release.
MIT License
Copyright (c) 2019 zmtzawqlp
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
\ No newline at end of file
# waterfall_flow
[![pub package](https://img.shields.io/pub/v/waterfall_flow.svg)](https://pub.dartlang.org/packages/waterfall_flow) [![GitHub stars](https://img.shields.io/github/stars/fluttercandies/waterfall_flow)](https://github.com/fluttercandies/waterfall_flow/stargazers) [![GitHub forks](https://img.shields.io/github/forks/fluttercandies/waterfall_flow)](https://github.com/fluttercandies/waterfall_flow/network) [![GitHub license](https://img.shields.io/github/license/fluttercandies/waterfall_flow)](https://github.com/fluttercandies/waterfall_flow/blob/master/LICENSE) [![GitHub issues](https://img.shields.io/github/issues/fluttercandies/waterfall_flow)](https://github.com/fluttercandies/waterfall_flow/issues) <a target="_blank" href="https://jq.qq.com/?_wv=1027&k=5bcc0gy"><img border="0" src="https://pub.idqqimg.com/wpa/images/group.png" alt="flutter-candies" title="flutter-candies"></a>
能够快速构建瀑布流布局的列表.
[Web demo for WaterfallFlow](https://fluttercandies.github.io/waterfall_flow/)
Language: [English](README.md) | 中文简体
- [waterfall_flow](#waterfall_flow)
- [使用](#使用)
- [简单使用](#简单使用)
- [列表元素回收](#列表元素回收)
- [ViewportBuilder](#viewportbuilder)
- [LastChildLayoutTypeBuilder](#lastchildlayouttypebuilder)
- [CloseToTrailing](#closetotrailing)
## 使用
* 在pubspec.yaml中增加库引用
```yaml
dependencies:
waterfall_flow: any
```
* 导入库
```dart
import 'package:waterfall_flow/waterfall_flow.dart';
```
## 简单使用
| ![img](https://github.com/fluttercandies/flutter_candies/tree/master/gif/waterfall_flow/random_sized.gif) | ![img](https://github.com/fluttercandies/flutter_candies/tree/master/gif/waterfall_flow/custom_scrollView.gif) |
| --------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| ![img](https://github.com/fluttercandies/flutter_candies/tree/master/gif/waterfall_flow/known_sized.gif) | ![img](https://github.com/fluttercandies/flutter_candies/tree/master/gif/waterfall_flow/variable_sized.gif) |
你可以通过设置SliverWaterfallFlowDelegate参数来定义瀑布流
* SliverWaterfallFlowDelegateWithFixedCrossAxisCount
| 参数 | 描述 | 默认 |
| -------------- | -------------------- | ------- |
| crossAxisCount | 横轴的等长度元素数量 | 必填 |
* SliverWaterfallFlowDelegateWithMaxCrossAxisExtent
| 参数 | 描述 | 默认 |
| ------------------ | ---------------------------------------------- | -------- |
| maxCrossAxisExtent | 横轴元素最大的大小. | required |
* SliverWaterfallFlowDelegate
| 参数 | 描述 | 默认 |
| ------------------ | ---------------------------------------------- | -------- |
| mainAxisSpacing | 主轴元素之间的距离 | 0.0|
| crossAxisSpacing | 横轴元素之间的距离 | 0.0|
| collectGarbage | 元素回收时候的回调 | - |
| lastChildLayoutTypeBuilder | 最后一个元素的布局样式(详情请查看后面) | - |
| viewportBuilder | 可视区域中元素indexes变化时的回调 | - |
| closeToTrailing | 可否让布局紧贴trailing(详情请查看后面) | false|
```dart
WaterfallFlow.builder(
//cacheExtent: 0.0,
padding: EdgeInsets.all(5.0),
gridDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5.0,
/// follow max child trailing layout offset and layout with full cross axis extend
/// last child as loadmore item/no more item in [GridView] and [WaterfallFlow]
/// with full cross axis extend
// LastChildLayoutType.fullCrossAxisExtend,
/// as foot at trailing and layout with full cross axis extend
/// show no more item at trailing when children are not full of viewport
/// if children is full of viewport, it's the same as fullCrossAxisExtend
// LastChildLayoutType.foot,
lastChildLayoutTypeBuilder: (index) => index == _list.length
? LastChildLayoutType.foot
: LastChildLayoutType.none,
),
```
## 列表元素回收
追踪列表元素回收,你可以在这个时刻回收一些内存,比如图片的内存缓存。
[更多详情](https://github.com/fluttercandies/extended_image/blob/e1577bc4d0b57c725110a9d886703b98a72772b5/example/lib/pages/photo_view_demo.dart#L91)
```dart
WaterfallFlow.builder(
gridDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
collectGarbage: (List<int> garbages) {
///collectGarbage
garbages.forEach((index) {
final provider = ExtendedNetworkImageProvider(
_list[index].imageUrl,
);
provider.evict();
});
},
),
```
## ViewportBuilder
追踪进入Viewport的列表元素的index(即你看到的可视区域,并不包括缓存距离)
```dart
WaterfallFlow.builder(
gridDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
viewportBuilder: (int firstIndex, int lastIndex) {
print("viewport : [$firstIndex,$lastIndex]");
}),
```
## LastChildLayoutTypeBuilder
为最后一个元素创建特殊布局,这主要是用在将最后一个元素作为loadmore/no more的时候。
```dart
enum LastChildLayoutType {
/// 普通的
none,
/// 将最后一个元素绘制在最大主轴Item之后,并且使用横轴大小最为layout size
/// 主要使用在[ExtendedGridView] and [WaterfallFlow]中,最后一个元素作为loadmore/no more元素的时候。
fullCrossAxisExtend,
/// 将最后一个child绘制在trailing of viewport,并且使用横轴大小最为layout size
/// 这种常用于最后一个元素作为loadmore/no more元素,并且列表元素没有充满整个viewport的时候
/// 如果列表元素充满viewport,那么效果跟fullCrossAxisExtend一样
foot,
}
WaterfallFlow.builder(
gridDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
lastChildLayoutTypeBuilder: (index) => index == length
? LastChildLayoutType.foot
: LastChildLayoutType.none,
),
```
## CloseToTrailing
当reverse设置为true的时候,布局会变成如下。常用于聊天列表,新的会话会被插入0的位置,但是当会话没有充满viewport的时候,下面的布局不是我们想要的。
```
trailing
-----------------
| |
| |
| item2 |
| item1 |
| item0 |
-----------------
leading
```
为了解决这个问题,你可以设置 closeToTrailing 为true, 布局将变成如下
该属性同时支持[ExtendedGridView],[ExtendedList],[WaterfallFlow]。
当然如果reverse如果不为ture,你设置这个属性依然会生效,没满viewport的时候布局会紧靠trailing
```
trailing
-----------------
| item2 |
| item1 |
| item0 |
| |
| |
-----------------
leading
```
```dart
WaterfallFlow.builder(
reverse: true,
gridDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(closeToTrailing: true),
```
## ☕️Buy me a coffee
![img](http://zmtzawqlp.gitee.io/my_images/images/qrcode.png)
# waterfall_flow
[![pub package](https://img.shields.io/pub/v/waterfall_flow.svg)](https://pub.dartlang.org/packages/waterfall_flow) [![GitHub stars](https://img.shields.io/github/stars/fluttercandies/waterfall_flow)](https://github.com/fluttercandies/waterfall_flow/stargazers) [![GitHub forks](https://img.shields.io/github/forks/fluttercandies/waterfall_flow)](https://github.com/fluttercandies/waterfall_flow/network) [![GitHub license](https://img.shields.io/github/license/fluttercandies/waterfall_flow)](https://github.com/fluttercandies/waterfall_flow/blob/master/LICENSE) [![GitHub issues](https://img.shields.io/github/issues/fluttercandies/waterfall_flow)](https://github.com/fluttercandies/waterfall_flow/issues) <a target="_blank" href="https://jq.qq.com/?_wv=1027&k=5bcc0gy"><img border="0" src="https://pub.idqqimg.com/wpa/images/group.png" alt="flutter-candies" title="flutter-candies"></a>
A Flutter grid view easy to build waterfall flow layout quickly.
[Web demo for WaterfallFlow](https://fluttercandies.github.io/waterfall_flow/)
Language: English | [中文简体](README-ZH.md)
- [waterfall_flow](#waterfall_flow)
- [Use](#use)
- [Easy to use](#easy-to-use)
- [CollectGarbage](#collectgarbage)
- [ViewportBuilder](#viewportbuilder)
- [LastChildLayoutTypeBuilder](#lastchildlayouttypebuilder)
- [CloseToTrailing](#closetotrailing)
## Use
* add library to your pubspec.yaml
```yaml
dependencies:
waterfall_flow: any
```
* import library in dart file
```dart
import 'package:waterfall_flow/waterfall_flow.dart';
```
## Easy to use
| ![img](https://github.com/fluttercandies/flutter_candies/tree/master/gif/waterfall_flow/random_sized.gif) | ![img](https://github.com/fluttercandies/flutter_candies/tree/master/gif/waterfall_flow/custom_scrollView.gif) |
| --------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| ![img](https://github.com/fluttercandies/flutter_candies/tree/master/gif/waterfall_flow/known_sized.gif) | ![img](https://github.com/fluttercandies/flutter_candies/tree/master/gif/waterfall_flow/variable_sized.gif) |
you can define waterfall flow layout within SliverWaterfallFlowDelegate.
* SliverWaterfallFlowDelegateWithFixedCrossAxisCount
| parameter | description | default |
| -------------- | ----------------------------------------- | -------- |
| crossAxisCount | The number of children in the cross axis. | required |
* SliverWaterfallFlowDelegateWithMaxCrossAxisExtent
| parameter | description | default |
| ------------------ | ---------------------------------------------- | -------- |
| maxCrossAxisExtent | The maximum extent of tiles in the cross axis. | required |
* SliverWaterfallFlowDelegate
| parameter | description | default |
| ------------------ | ---------------------------------------------- | -------- |
| mainAxisSpacing | The number of logical pixels between each child along the main axis. | 0.0 |
| crossAxisSpacing | The number of logical pixels between each child along the cross axis. | 0.0 |
| collectGarbage | Call when collect garbage, return indexs to collect | - |
| lastChildLayoutTypeBuilder | The builder to get layout type of last child ,Notice: it should only for last child | - |
| viewportBuilder | The builder to get indexs in viewport | - |
| closeToTrailing | Whether make layout close to trailing | false |
```dart
WaterfallFlow.builder(
//cacheExtent: 0.0,
padding: EdgeInsets.all(5.0),
gridDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5.0,
/// follow max child trailing layout offset and layout with full cross axis extend
/// last child as loadmore item/no more item in [GridView] and [WaterfallFlow]
/// with full cross axis extend
// LastChildLayoutType.fullCrossAxisExtend,
/// as foot at trailing and layout with full cross axis extend
/// show no more item at trailing when children are not full of viewport
/// if children is full of viewport, it's the same as fullCrossAxisExtend
// LastChildLayoutType.foot,
lastChildLayoutTypeBuilder: (index) => index == _list.length
? LastChildLayoutType.foot
: LastChildLayoutType.none,
),
```
## CollectGarbage
track the indexes are collect, you can collect garbage at that monment(for example Image cache)
[more detail](https://github.com/fluttercandies/extended_image/blob/e1577bc4d0b57c725110a9d886703b98a72772b5/example/lib/pages/photo_view_demo.dart#L91)
```dart
WaterfallFlow.builder(
gridDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
collectGarbage: (List<int> garbages) {
///collectGarbage
garbages.forEach((index) {
final provider = ExtendedNetworkImageProvider(
_list[index].imageUrl,
);
provider.evict();
});
},
),
```
## ViewportBuilder
track the indexes go into the viewport, it's not include cache extent.
```dart
WaterfallFlow.builder(
gridDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
viewportBuilder: (int firstIndex, int lastIndex) {
print("viewport : [$firstIndex,$lastIndex]");
}),
```
## LastChildLayoutTypeBuilder
build lastChild as special child in the case that it is loadmore/no more item.
```dart
enum LastChildLayoutType {
/// as default child
none,
/// follow max child trailing layout offset and layout with full cross axis extend
/// last child as loadmore item/no more item in [ExtendedGridView] and [WaterfallFlow]
/// with full cross axis extend
fullCrossAxisExtend,
/// as foot at trailing and layout with full cross axis extend
/// show no more item at trailing when children are not full of viewport
/// if children is full of viewport, it's the same as fullCrossAxisExtend
foot,
}
WaterfallFlow.builder(
gridDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
lastChildLayoutTypeBuilder: (index) => index == length
? LastChildLayoutType.foot
: LastChildLayoutType.none,
),
```
## CloseToTrailing
when reverse property of List is true, layout is as following.
it likes chat list, and new session will insert to zero index.
but it's not right when items are not full of viewport.
```
trailing
-----------------
| |
| |
| item2 |
| item1 |
| item0 |
-----------------
leading
```
to solve it, you could set closeToTrailing to true, layout is as following.
support [ExtendedGridView],[ExtendedList],[WaterfallFlow].
and it also works when reverse is flase, layout will close to trailing.
```
trailing
-----------------
| item2 |
| item1 |
| item0 |
| |
| |
-----------------
leading
```
```dart
WaterfallFlow.builder(
reverse: true,
gridDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(closeToTrailing: true),
```
# Specify analysis options.
#
# Until there are meta linter rules, each desired lint must be explicitly enabled.
# See: https://github.com/dart-lang/linter/issues/288
#
# For a list of lints, see: http://dart-lang.github.io/linter/lints/
# See the configuration guide for more
# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer
#
# There are other similar analysis options files in the flutter repos,
# which should be kept in sync with this file:
#
# - analysis_options.yaml (this file)
# - packages/flutter/lib/analysis_options_user.yaml
# - https://github.com/flutter/plugins/blob/master/analysis_options.yaml
# - https://github.com/flutter/engine/blob/master/analysis_options.yaml
#
# This file contains the analysis options used by Flutter tools, such as IntelliJ,
# Android Studio, and the `flutter analyze` command.
analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
errors:
# treat missing required parameters as a warning (not a hint)
missing_required_param: warning
# treat missing returns as a warning (not a hint)
missing_return: warning
# allow having TODOs in the code
todo: ignore
# Ignore analyzer hints for updating pubspecs when using Future or
# Stream and not importing dart:async
# Please see https://github.com/flutter/flutter/pull/24528 for details.
sdk_version_async_exported_from_core: ignore
# exclude:
# - "bin/cache/**"
# # the following two are relative to the stocks example and the flutter package respectively
# # see https://github.com/dart-lang/sdk/issues/28463
# - "lib/i18n/messages_*.dart"
# - "lib/src/http/**"
linter:
rules:
# these rules are documented on and in the same order as
# the Dart Lint rules page to make maintenance easier
# https://github.com/dart-lang/linter/blob/master/example/all.yaml
- always_declare_return_types
- always_put_control_body_on_new_line
# - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219
- always_require_non_null_named_parameters
- always_specify_types
- annotate_overrides
# - avoid_annotating_with_dynamic # conflicts with always_specify_types
# - avoid_as # required for implicit-casts: true
- avoid_bool_literals_in_conditional_expressions
# - avoid_catches_without_on_clauses # we do this commonly
# - avoid_catching_errors # we do this commonly
- avoid_classes_with_only_static_members
# - avoid_double_and_int_checks # only useful when targeting JS runtime
- avoid_empty_else
# - avoid_equals_and_hash_code_on_mutable_classes # not yet tested
- avoid_field_initializers_in_const_classes
- avoid_function_literals_in_foreach_calls
# - avoid_implementing_value_types # not yet tested
- avoid_init_to_null
# - avoid_js_rounded_ints # only useful when targeting JS runtime
- avoid_null_checks_in_equality_operators
# - avoid_positional_boolean_parameters # not yet tested
# - avoid_print # not yet tested
# - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356)
# - avoid_redundant_argument_values # not yet tested
- avoid_relative_lib_imports
- avoid_renaming_method_parameters
- avoid_return_types_on_setters
# - avoid_returning_null # there are plenty of valid reasons to return null
# - avoid_returning_null_for_future # not yet tested
- avoid_returning_null_for_void
# - avoid_returning_this # there are plenty of valid reasons to return this
# - avoid_setters_without_getters # not yet tested
# - avoid_shadowing_type_parameters # not yet tested
- avoid_single_cascade_in_expression_statements
- avoid_slow_async_io
- avoid_types_as_parameter_names
# - avoid_types_on_closure_parameters # conflicts with always_specify_types
# - avoid_unnecessary_containers # not yet tested
- avoid_unused_constructor_parameters
- avoid_void_async
# - avoid_web_libraries_in_flutter # not yet tested
- await_only_futures
- camel_case_extensions
- camel_case_types
- cancel_subscriptions
# - cascade_invocations # not yet tested
# - close_sinks # not reliable enough
# - comment_references # blocked on https://github.com/flutter/flutter/issues/20765
# - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204
- control_flow_in_finally
# - curly_braces_in_flow_control_structures # not yet tested
# - diagnostic_describe_all_properties # not yet tested
- directives_ordering
- empty_catches
- empty_constructor_bodies
- empty_statements
# - file_names # not yet tested
- flutter_style_todos
- hash_and_equals
- implementation_imports
# - invariant_booleans # too many false positives: https://github.com/dart-lang/linter/issues/811
- iterable_contains_unrelated_type
# - join_return_with_assignment # not yet tested
- library_names
- library_prefixes
# - lines_longer_than_80_chars # not yet tested
- list_remove_unrelated_type
# - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181
# - missing_whitespace_between_adjacent_strings # not yet tested
- no_adjacent_strings_in_list
- no_duplicate_case_values
# - no_logic_in_create_state # not yet tested
# - no_runtimeType_toString # not yet tested
- non_constant_identifier_names
# - null_closures # not yet tested
# - omit_local_variable_types # opposite of always_specify_types
# - one_member_abstracts # too many false positives
# - only_throw_errors # https://github.com/flutter/flutter/issues/5792
- overridden_fields
- package_api_docs
- package_names
- package_prefixed_library_names
# - parameter_assignments # we do this commonly
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
# - prefer_asserts_with_message # not yet tested
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
# - prefer_constructors_over_static_methods # not yet tested
- prefer_contains
# - prefer_double_quotes # opposite of prefer_single_quotes
- prefer_equal_for_default_values
# - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
- prefer_final_fields
- prefer_final_in_for_each
- prefer_final_locals
- prefer_for_elements_to_map_fromIterable
- prefer_foreach
# - prefer_function_declarations_over_variables # not yet tested
- prefer_generic_function_type_aliases
- prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
# - prefer_int_literals # not yet tested
# - prefer_interpolation_to_compose_strings # not yet tested
- prefer_is_empty
- prefer_is_not_empty
- prefer_is_not_operator
- prefer_iterable_whereType
# - prefer_mixin # https://github.com/dart-lang/language/issues/32
# - prefer_null_aware_operators # disable until NNBD, see https://github.com/flutter/flutter/pull/32711#issuecomment-492930932
# - prefer_relative_imports # not yet tested
- prefer_single_quotes
- prefer_spread_collections
- prefer_typing_uninitialized_variables
- prefer_void_to_null
# - provide_deprecation_message # not yet tested
# - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml
- recursive_getters
- slash_for_doc_comments
# - sort_child_properties_last # not yet tested
- sort_constructors_first
- sort_pub_dependencies
- sort_unnamed_constructors_first
- test_types_in_equals
- throw_in_finally
# - type_annotate_public_apis # subset of always_specify_types
- type_init_formals
# - unawaited_futures # too many false positives
# - unnecessary_await_in_return # not yet tested
- unnecessary_brace_in_string_interps
- unnecessary_const
# - unnecessary_final # conflicts with prefer_final_locals
- unnecessary_getters_setters
# - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_in_if_null_operators
- unnecessary_overrides
- unnecessary_parenthesis
- unnecessary_statements
- unnecessary_string_interpolations
- unnecessary_this
- unrelated_type_equality_checks
# - unsafe_html # not yet tested
- use_full_hex_values_for_flutter_colors
# - use_function_type_syntax_for_parameters # not yet tested
# - use_key_in_widget_constructors # not yet tested
- use_rethrow_when_possible
# - use_setters_to_change_properties # not yet tested
# - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182
# - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
- valid_regexps
- void_checks
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"path_provider","path":"/Users/qiaomeng/flutter/.pub-cache/hosted/pub.flutter-io.cn/path_provider-1.6.28/","dependencies":[]},{"name":"url_launcher","path":"/Users/qiaomeng/flutter/.pub-cache/hosted/pub.flutter-io.cn/url_launcher-5.3.0/","dependencies":[]}],"android":[{"name":"path_provider","path":"/Users/qiaomeng/flutter/.pub-cache/hosted/pub.flutter-io.cn/path_provider-1.6.28/","dependencies":[]},{"name":"url_launcher","path":"/Users/qiaomeng/flutter/.pub-cache/hosted/pub.flutter-io.cn/url_launcher-5.3.0/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/qiaomeng/flutter/.pub-cache/hosted/pub.flutter-io.cn/path_provider_macos-0.0.4+8/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/qiaomeng/flutter/.pub-cache/hosted/pub.flutter-io.cn/path_provider_linux-0.0.1+2/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/qiaomeng/flutter/.pub-cache/hosted/pub.flutter-io.cn/path_provider_windows-0.0.4+3/","dependencies":[]}],"web":[{"name":"url_launcher_web","path":"/Users/qiaomeng/flutter/.pub-cache/hosted/pub.flutter-io.cn/url_launcher_web-0.1.5+3/","dependencies":[]}]},"dependencyGraph":[{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_web"]},{"name":"url_launcher_web","dependencies":[]}],"date_created":"2021-09-28 16:57:14.801573","version":"1.22.4"}
\ No newline at end of file
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Exceptions to above rules.
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
.packages
pubspec.lock
\ No newline at end of file
# 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: ee032f67c734e607d8ea5c870ba744daf4bf56e7
channel: master
project_type: app
# 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://flutter.dev/docs/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
For help getting started with Flutter, view our
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
# Specify analysis options.
#
# Until there are meta linter rules, each desired lint must be explicitly enabled.
# See: https://github.com/dart-lang/linter/issues/288
#
# For a list of lints, see: http://dart-lang.github.io/linter/lints/
# See the configuration guide for more
# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer
#
# There are other similar analysis options files in the flutter repos,
# which should be kept in sync with this file:
#
# - analysis_options.yaml (this file)
# - packages/flutter/lib/analysis_options_user.yaml
# - https://github.com/flutter/plugins/blob/master/analysis_options.yaml
# - https://github.com/flutter/engine/blob/master/analysis_options.yaml
#
# This file contains the analysis options used by Flutter tools, such as IntelliJ,
# Android Studio, and the `flutter analyze` command.
analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
errors:
# treat missing required parameters as a warning (not a hint)
missing_required_param: warning
# treat missing returns as a warning (not a hint)
missing_return: warning
# allow having TODOs in the code
todo: ignore
# Ignore analyzer hints for updating pubspecs when using Future or
# Stream and not importing dart:async
# Please see https://github.com/flutter/flutter/pull/24528 for details.
sdk_version_async_exported_from_core: ignore
exclude:
- "lib/common/data/mock_data.dart"
# # the following two are relative to the stocks example and the flutter package respectively
# # see https://github.com/dart-lang/sdk/issues/28463
# - "lib/i18n/messages_*.dart"
# - "lib/src/http/**"
linter:
rules:
# these rules are documented on and in the same order as
# the Dart Lint rules page to make maintenance easier
# https://github.com/dart-lang/linter/blob/master/example/all.yaml
- always_declare_return_types
- always_put_control_body_on_new_line
# - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219
- always_require_non_null_named_parameters
- always_specify_types
- annotate_overrides
# - avoid_annotating_with_dynamic # conflicts with always_specify_types
# - avoid_as # required for implicit-casts: true
- avoid_bool_literals_in_conditional_expressions
# - avoid_catches_without_on_clauses # we do this commonly
# - avoid_catching_errors # we do this commonly
- avoid_classes_with_only_static_members
# - avoid_double_and_int_checks # only useful when targeting JS runtime
- avoid_empty_else
# - avoid_equals_and_hash_code_on_mutable_classes # not yet tested
- avoid_field_initializers_in_const_classes
- avoid_function_literals_in_foreach_calls
# - avoid_implementing_value_types # not yet tested
- avoid_init_to_null
# - avoid_js_rounded_ints # only useful when targeting JS runtime
- avoid_null_checks_in_equality_operators
# - avoid_positional_boolean_parameters # not yet tested
# - avoid_print # not yet tested
# - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356)
# - avoid_redundant_argument_values # not yet tested
- avoid_relative_lib_imports
- avoid_renaming_method_parameters
- avoid_return_types_on_setters
# - avoid_returning_null # there are plenty of valid reasons to return null
# - avoid_returning_null_for_future # not yet tested
- avoid_returning_null_for_void
# - avoid_returning_this # there are plenty of valid reasons to return this
# - avoid_setters_without_getters # not yet tested
# - avoid_shadowing_type_parameters # not yet tested
- avoid_single_cascade_in_expression_statements
- avoid_slow_async_io
- avoid_types_as_parameter_names
# - avoid_types_on_closure_parameters # conflicts with always_specify_types
# - avoid_unnecessary_containers # not yet tested
- avoid_unused_constructor_parameters
- avoid_void_async
# - avoid_web_libraries_in_flutter # not yet tested
- await_only_futures
- camel_case_extensions
- camel_case_types
- cancel_subscriptions
# - cascade_invocations # not yet tested
# - close_sinks # not reliable enough
# - comment_references # blocked on https://github.com/flutter/flutter/issues/20765
# - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204
- control_flow_in_finally
# - curly_braces_in_flow_control_structures # not yet tested
# - diagnostic_describe_all_properties # not yet tested
- directives_ordering
- empty_catches
- empty_constructor_bodies
- empty_statements
# - file_names # not yet tested
- flutter_style_todos
- hash_and_equals
- implementation_imports
# - invariant_booleans # too many false positives: https://github.com/dart-lang/linter/issues/811
- iterable_contains_unrelated_type
# - join_return_with_assignment # not yet tested
- library_names
- library_prefixes
# - lines_longer_than_80_chars # not yet tested
- list_remove_unrelated_type
# - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181
# - missing_whitespace_between_adjacent_strings # not yet tested
- no_adjacent_strings_in_list
- no_duplicate_case_values
# - no_logic_in_create_state # not yet tested
# - no_runtimeType_toString # not yet tested
- non_constant_identifier_names
# - null_closures # not yet tested
# - omit_local_variable_types # opposite of always_specify_types
# - one_member_abstracts # too many false positives
# - only_throw_errors # https://github.com/flutter/flutter/issues/5792
- overridden_fields
- package_api_docs
- package_names
- package_prefixed_library_names
# - parameter_assignments # we do this commonly
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
# - prefer_asserts_with_message # not yet tested
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
# - prefer_constructors_over_static_methods # not yet tested
- prefer_contains
# - prefer_double_quotes # opposite of prefer_single_quotes
- prefer_equal_for_default_values
# - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
- prefer_final_fields
- prefer_final_in_for_each
- prefer_final_locals
- prefer_for_elements_to_map_fromIterable
- prefer_foreach
# - prefer_function_declarations_over_variables # not yet tested
- prefer_generic_function_type_aliases
- prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
# - prefer_int_literals # not yet tested
# - prefer_interpolation_to_compose_strings # not yet tested
- prefer_is_empty
- prefer_is_not_empty
- prefer_is_not_operator
- prefer_iterable_whereType
# - prefer_mixin # https://github.com/dart-lang/language/issues/32
# - prefer_null_aware_operators # disable until NNBD, see https://github.com/flutter/flutter/pull/32711#issuecomment-492930932
# - prefer_relative_imports # not yet tested
- prefer_single_quotes
- prefer_spread_collections
- prefer_typing_uninitialized_variables
- prefer_void_to_null
# - provide_deprecation_message # not yet tested
# - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml
- recursive_getters
- slash_for_doc_comments
# - sort_child_properties_last # not yet tested
- sort_constructors_first
- sort_pub_dependencies
- sort_unnamed_constructors_first
- test_types_in_equals
- throw_in_finally
# - type_annotate_public_apis # subset of always_specify_types
- type_init_formals
# - unawaited_futures # too many false positives
# - unnecessary_await_in_return # not yet tested
- unnecessary_brace_in_string_interps
- unnecessary_const
# - unnecessary_final # conflicts with prefer_final_locals
- unnecessary_getters_setters
# - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_in_if_null_operators
- unnecessary_overrides
- unnecessary_parenthesis
- unnecessary_statements
- unnecessary_string_interpolations
- unnecessary_this
- unrelated_type_equality_checks
# - unsafe_html # not yet tested
- use_full_hex_values_for_flutter_colors
# - use_function_type_syntax_for_parameters # not yet tested
# - use_key_in_widget_constructors # not yet tested
- use_rethrow_when_possible
# - use_setters_to_change_properties # not yet tested
# - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182
# - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
- valid_regexps
- void_checks
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 29
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.example"
minSdkVersion 16
targetSdkVersion 29
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name="io.flutter.app.FlutterApplication"
android:label="example"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual
gap between the end of Android's launch screen and the painting of
Flutter's first frame. -->
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
package com.example.example
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@android:color/white</item>
</style>
</resources>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true
android.enableR8=true
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
- t f -o lib/common -s
\ No newline at end of file
--route-constants --route-names --route-helper --git flutter_candies_demo_library --no-is-initial-route
\ No newline at end of file
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>9.0</string>
</dict>
</plist>
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :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__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
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 */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>example</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
#import "GeneratedPluginRegistrant.h"
// GENERATED CODE - DO NOT MODIFY MANUALLY
// **************************************************************************
// Auto generated by https://github.com/fluttercandies/ff_annotation_route
// **************************************************************************
import 'package:ff_annotation_route/ff_annotation_route.dart';
import 'package:flutter/widgets.dart';
import 'pages/main_page.dart';
import 'pages/simple/custom_scrollView_demo.dart';
import 'pages/simple/random_sized_demo.dart';
RouteResult getRouteResult({String name, Map<String, dynamic> arguments}) {
arguments = arguments ?? const <String, dynamic>{};
switch (name) {
case 'fluttercandies://custom_scrollview':
return RouteResult(
name: name,
widget: CustomScrollviewDemo(),
routeName: 'custom_scrollview',
description: 'show how to build waterfall flow in CustomScrollview.',
exts: <String, dynamic>{'group': 'Simple', 'order': 1},
);
case 'fluttercandies://demogrouppage':
return RouteResult(
name: name,
widget: DemoGroupPage(
keyValue:
arguments['keyValue'] as MapEntry<String, List<DemoRouteResult>>,
),
routeName: 'DemoGroupPage',
);
case 'fluttercandies://known-sized':
return RouteResult(
name: name,
routeName: 'known-sized',
description:
'show how to build a known-sized item with waterfall flow list.',
exts: <String, dynamic>{'group': 'Complex', 'order': 1},
);
case 'fluttercandies://mainpage':
return RouteResult(
name: name,
widget: MainPage(),
routeName: 'MainPage',
);
case 'fluttercandies://random-sized':
return RouteResult(
name: name,
widget: RandomSizedDemo(),
routeName: 'random-sized',
description:
'show how to build random-sized item with waterfall flow list.',
exts: <String, dynamic>{'group': 'Simple', 'order': 0},
);
case 'fluttercandies://variable-sized':
return RouteResult(
name: name,
routeName: 'variable-sized',
description:
'show how to build a variable-sized item with waterfall flow list.',
exts: <String, dynamic>{'group': 'Complex', 'order': 1},
);
default:
return const RouteResult(name: 'flutterCandies://notfound');
}
}
class RouteResult {
const RouteResult({
@required this.name,
this.widget,
this.showStatusBar = true,
this.routeName = '',
this.pageRouteType,
this.description = '',
this.exts,
});
/// The name of the route (e.g., "/settings").
///
/// If null, the route is anonymous.
final String name;
/// The Widget return base on route
final Widget widget;
/// Whether show this route with status bar.
final bool showStatusBar;
/// The route name to track page
final String routeName;
/// The type of page route
final PageRouteType pageRouteType;
/// The description of route
final String description;
/// The extend arguments
final Map<String, dynamic> exts;
}
// GENERATED CODE - DO NOT MODIFY MANUALLY
// **************************************************************************
// Auto generated by https://github.com/fluttercandies/ff_annotation_route
// **************************************************************************
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:ff_annotation_route/ff_annotation_route.dart';
import 'example_route.dart';
class FFNavigatorObserver extends NavigatorObserver {
FFNavigatorObserver({this.routeChange});
final RouteChange routeChange;
@override
void didPop(Route<dynamic> route, Route<dynamic> previousRoute) {
super.didPop(route, previousRoute);
_didRouteChange(previousRoute, route);
}
@override
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
super.didPush(route, previousRoute);
_didRouteChange(route, previousRoute);
}
@override
void didRemove(Route<dynamic> route, Route<dynamic> previousRoute) {
super.didRemove(route, previousRoute);
_didRouteChange(previousRoute, route);
}
@override
void didReplace({Route<dynamic> newRoute, Route<dynamic> oldRoute}) {
super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
_didRouteChange(newRoute, oldRoute);
}
void _didRouteChange(Route<dynamic> newRoute, Route<dynamic> oldRoute) {
// oldRoute may be null when route first time enter.
routeChange?.call(newRoute, oldRoute);
}
}
typedef RouteChange = void Function(
Route<dynamic> newRoute, Route<dynamic> oldRoute);
class FFTransparentPageRoute<T> extends PageRouteBuilder<T> {
FFTransparentPageRoute({
RouteSettings settings,
@required RoutePageBuilder pageBuilder,
RouteTransitionsBuilder transitionsBuilder = _defaultTransitionsBuilder,
Duration transitionDuration = const Duration(milliseconds: 150),
bool barrierDismissible = false,
Color barrierColor,
String barrierLabel,
bool maintainState = true,
}) : assert(pageBuilder != null),
assert(transitionsBuilder != null),
assert(barrierDismissible != null),
assert(maintainState != null),
super(
settings: settings,
opaque: false,
pageBuilder: pageBuilder,
transitionsBuilder: transitionsBuilder,
transitionDuration: transitionDuration,
barrierDismissible: barrierDismissible,
barrierColor: barrierColor,
barrierLabel: barrierLabel,
maintainState: maintainState,
);
}
Widget _defaultTransitionsBuilder(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return FadeTransition(
opacity: CurvedAnimation(
parent: animation,
curve: Curves.easeOut,
),
child: child,
);
}
Route<dynamic> onGenerateRouteHelper(
RouteSettings settings, {
Widget notFoundFallback,
Object arguments,
WidgetBuilder builder,
}) {
arguments ??= settings.arguments;
final RouteResult routeResult = getRouteResult(
name: settings.name,
arguments: arguments as Map<String, dynamic>,
);
if (routeResult.showStatusBar != null || routeResult.routeName != null) {
settings = FFRouteSettings(
name: settings.name,
routeName: routeResult.routeName,
arguments: arguments as Map<String, dynamic>,
showStatusBar: routeResult.showStatusBar,
);
}
Widget page = routeResult.widget ?? notFoundFallback;
if (page == null) {
throw Exception(
'''Route "${settings.name}" returned null. Route Widget must never return null,
maybe the reason is that route name did not match with right path.
You can use parameter[notFoundFallback] to avoid this ugly error.''',
);
}
if (arguments is Map<String, dynamic>) {
final RouteBuilder builder = arguments['routeBuilder'] as RouteBuilder;
if (builder != null) {
return builder(page);
}
}
if (builder != null) {
page = builder(page, routeResult);
}
switch (routeResult.pageRouteType) {
case PageRouteType.material:
return MaterialPageRoute<dynamic>(
settings: settings,
builder: (BuildContext _) => page,
);
case PageRouteType.cupertino:
return CupertinoPageRoute<dynamic>(
settings: settings,
builder: (BuildContext _) => page,
);
case PageRouteType.transparent:
return FFTransparentPageRoute<dynamic>(
settings: settings,
pageBuilder: (
BuildContext _,
Animation<double> __,
Animation<double> ___,
) =>
page,
);
default:
return kIsWeb || !Platform.isIOS
? MaterialPageRoute<dynamic>(
settings: settings,
builder: (BuildContext _) => page,
)
: CupertinoPageRoute<dynamic>(
settings: settings,
builder: (BuildContext _) => page,
);
}
}
typedef RouteBuilder = PageRoute<dynamic> Function(Widget page);
class FFRouteSettings extends RouteSettings {
const FFRouteSettings({
this.routeName,
this.showStatusBar,
String name,
Object arguments,
}) : super(
name: name,
arguments: arguments,
);
final String routeName;
final bool showStatusBar;
}
/// Signature for a function that creates a widget, e.g.
typedef WidgetBuilder = Widget Function(Widget child, RouteResult routeResult);
// GENERATED CODE - DO NOT MODIFY MANUALLY
// **************************************************************************
// Auto generated by https://github.com/fluttercandies/ff_annotation_route
// **************************************************************************
const List<String> routeNames = <String>[
'fluttercandies://custom_scrollview',
'fluttercandies://demogrouppage',
'fluttercandies://known-sized',
'fluttercandies://mainpage',
'fluttercandies://random-sized',
'fluttercandies://variable-sized',
];
class Routes {
const Routes._();
/// 'show how to build waterfall flow in CustomScrollview.'
///
/// [name] : 'fluttercandies://custom_scrollview'
///
/// [routeName] : 'custom_scrollview'
///
/// [description] : 'show how to build waterfall flow in CustomScrollview.'
///
/// [exts] : {group: Simple, order: 1}
static const String fluttercandiesCustomScrollview =
'fluttercandies://custom_scrollview';
/// 'DemoGroupPage'
///
/// [name] : 'fluttercandies://demogrouppage'
///
/// [routeName] : 'DemoGroupPage'
///
/// [constructors] :
///
/// DemoGroupPage : [MapEntry<String, List<DemoRouteResult>> keyValue]
static const String fluttercandiesDemogrouppage =
'fluttercandies://demogrouppage';
/// 'show how to build a known-sized item with waterfall flow list.'
///
/// [name] : 'fluttercandies://known-sized'
///
/// [routeName] : 'known-sized'
///
/// [description] : 'show how to build a known-sized item with waterfall flow list.'
///
/// [exts] : {group: Complex, order: 1}
static const String fluttercandiesKnownSized = 'fluttercandies://known-sized';
/// 'MainPage'
///
/// [name] : 'fluttercandies://mainpage'
///
/// [routeName] : 'MainPage'
///
/// [constructors] :
///
/// MainPage : []
static const String fluttercandiesMainpage = 'fluttercandies://mainpage';
/// 'show how to build random-sized item with waterfall flow list.'
///
/// [name] : 'fluttercandies://random-sized'
///
/// [routeName] : 'random-sized'
///
/// [description] : 'show how to build random-sized item with waterfall flow list.'
///
/// [exts] : {group: Simple, order: 0}
static const String fluttercandiesRandomSized =
'fluttercandies://random-sized';
/// 'show how to build a variable-sized item with waterfall flow list.'
///
/// [name] : 'fluttercandies://variable-sized'
///
/// [routeName] : 'variable-sized'
///
/// [description] : 'show how to build a variable-sized item with waterfall flow list.'
///
/// [exts] : {group: Complex, order: 1}
static const String fluttercandiesVariableSized =
'fluttercandies://variable-sized';
}
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_sliver_tracker/flutter_sliver_tracker.dart';
import 'package:waterfall_flow/waterfall_flow.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '封装曝光',
debugShowCheckedModeBanner: false,
theme: ThemeData(primarySwatch: Colors.blue),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({ Key key }) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title:const Text('组件曝光测试'),centerTitle: true,),
body: _buildBody(),
);
}
Widget _buildBody(){
return XXSliverWaterFallFlow(
onScrollInit:(int firstIndex, int lastIndex){
print('初始化显示的 =========> $firstIndex $lastIndex');
},
onScrollEnd:(int firstIndex, int lastIndex){
print('滑动结束显示的 =========> $firstIndex $lastIndex');
},
itemBuilder: (BuildContext context, int index) {
return Container(
height:120,
color: Colors.red.withAlpha(20 * index),
alignment: Alignment.center,
child: Text('$index'),
);
}
);
}
}
\ No newline at end of file
import 'package:example/example_routes.dart';
import 'package:flutter/material.dart';
import 'package:ff_annotation_route/ff_annotation_route.dart';
import 'package:collection/collection.dart';
import '../example_route.dart';
import '../example_routes.dart' as example_routes;
@FFRoute(
name: 'fluttercandies://mainpage',
routeName: 'MainPage',
)
class MainPage extends StatelessWidget {
MainPage() {
final List<String> routeNames = <String>[];
routeNames.addAll(example_routes.routeNames);
routeNames.remove(Routes.fluttercandiesMainpage);
routeNames.remove(Routes.fluttercandiesDemogrouppage);
routesGroup.addAll(groupBy<DemoRouteResult, String>(
routeNames
.map<RouteResult>((String name) => getRouteResult(name: name))
.where((RouteResult element) => element.exts != null)
.map<DemoRouteResult>((RouteResult e) => DemoRouteResult(e))
.toList()
..sort((DemoRouteResult a, DemoRouteResult b) =>
b.group.compareTo(a.group)),
(DemoRouteResult x) => x.group));
}
final Map<String, List<DemoRouteResult>> routesGroup =
<String, List<DemoRouteResult>>{};
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('曝光测试'),
),
body: ListView.builder(
itemBuilder: (BuildContext c, int index) {
final String type = routesGroup.keys.toList()[index];
return Container(
margin: const EdgeInsets.all(20.0),
child: GestureDetector(
behavior: HitTestBehavior.translucent,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
(index + 1).toString() + '.' + type,
//style: TextStyle(inherit: false),
),
Text(
'$type 曝光测试',
style: const TextStyle(color: Colors.grey),
)
],
),
onTap: () {
Navigator.pushNamed(
context, Routes.fluttercandiesDemogrouppage,
arguments: <String, dynamic>{
'keyValue': routesGroup.entries.toList()[index],
});
},
));
},
itemCount: routesGroup.length,
),
);
}
}
@FFRoute(
name: 'fluttercandies://demogrouppage',
routeName: 'DemoGroupPage',
)
class DemoGroupPage extends StatelessWidget {
DemoGroupPage({MapEntry<String, List<DemoRouteResult>> keyValue})
: routes = keyValue.value
..sort((DemoRouteResult a, DemoRouteResult b) =>
a.order.compareTo(b.order)),
group = keyValue.key;
final List<DemoRouteResult> routes;
final String group;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('$group demos'),
),
body: ListView.builder(
itemBuilder: (BuildContext context, int index) {
final DemoRouteResult page = routes[index];
return Container(
margin: const EdgeInsets.all(20.0),
child: GestureDetector(
behavior: HitTestBehavior.translucent,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
(index + 1).toString() + '.' + page.routeResult.routeName,
//style: TextStyle(inherit: false),
),
Text(
page.routeResult.description,
style: const TextStyle(color: Colors.grey),
)
],
),
onTap: () {
Navigator.pushNamed(context, page.routeResult.name);
},
),
);
},
itemCount: routes.length,
),
);
}
}
class DemoRouteResult {
DemoRouteResult(
this.routeResult,
) : order = routeResult.exts['order'] as int,
group = routeResult.exts['group'] as String;
final int order;
final String group;
final RouteResult routeResult;
}
///
/// create by zmtzawqlp on 2019/11/19
///
import 'dart:math';
import 'package:ff_annotation_route/ff_annotation_route.dart';
import 'package:flutter/material.dart';
import 'package:waterfall_flow/waterfall_flow.dart';
@FFRoute(
name: 'fluttercandies://custom_scrollview',
routeName: 'custom_scrollview',
description: 'show how to build waterfall flow in CustomScrollview.',
exts: <String, dynamic>{
'group': 'Simple',
'order': 1,
},
)
class CustomScrollviewDemo extends StatefulWidget {
@override
_CustomScrollviewDemoState createState() => _CustomScrollviewDemoState();
}
class _CustomScrollviewDemoState extends State<CustomScrollviewDemo> {
List<Color> colors = <Color>[];
int crossAxisCount = 4;
double crossAxisSpacing = 5.0;
double mainAxisSpacing = 5.0;
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
child: Container(
height: 200,
color: Colors.red,
alignment: Alignment.center,
child: const Text(
'I\'m other slivers',
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
),
),
SliverWaterfallFlow(
gridDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
crossAxisSpacing: crossAxisSpacing,
mainAxisSpacing: mainAxisSpacing,
collectGarbage: (List<int> garbages) {
print('collect garbage : $garbages');
},
viewportBuilder: (int firstIndex, int lastIndex) {
print('viewport : [$firstIndex,$lastIndex]');
},
),
delegate: SliverChildBuilderDelegate((BuildContext c, int index) {
final Color color = getRandomColor(index);
return Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: getRandomColor(index)),
alignment: Alignment.center,
child: Text(
'$index',
style: TextStyle(
color: color.computeLuminance() < 0.5
? Colors.white
: Colors.black),
),
//height: index == 5 ? 1500.0 : 100.0,
height: ((index % 3) + 1) * 100.0,
);
}),
)
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
crossAxisCount++;
//mainAxisSpacing+=5.0;
//crossAxisSpacing+=5.0;
});
},
child: const Icon(Icons.add),
),
);
}
Color getRandomColor(int index) {
if (index >= colors.length) {
colors.add(Color.fromARGB(255, Random.secure().nextInt(255),
Random.secure().nextInt(255), Random.secure().nextInt(255)));
}
return colors[index];
}
}
///
/// create by zmtzawqlp on 2019/11/19
///
import 'dart:math';
import 'package:ff_annotation_route/ff_annotation_route.dart';
import 'package:flutter/material.dart';
import 'package:waterfall_flow/waterfall_flow.dart';
@FFRoute(
name: 'fluttercandies://random-sized',
routeName: 'random-sized',
description: 'show how to build random-sized item with waterfall flow list.',
exts: <String, dynamic>{
'group': 'Simple',
'order': 0,
},
)
class RandomSizedDemo extends StatefulWidget {
@override
_RandomSizedDemoState createState() => _RandomSizedDemoState();
}
class _RandomSizedDemoState extends State<RandomSizedDemo> {
List<Color> colors = <Color>[];
int crossAxisCount = 4;
double crossAxisSpacing = 5.0;
double mainAxisSpacing = 5.0;
TextDirection textDirection = TextDirection.ltr;
int length = 100;
ScrollController controller = ScrollController();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Directionality(
textDirection: textDirection,
child: WaterfallFlow.builder(
//cacheExtent: 0.0,
//reverse: true,
padding: const EdgeInsets.all(5.0),
gridDelegate: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
crossAxisSpacing: crossAxisSpacing,
mainAxisSpacing: mainAxisSpacing,
// collectGarbage: (List<int> garbages) {
// print('collect garbage : $garbages');
// },
// viewportBuilder: (int firstIndex, int lastIndex) {
// print('viewport : [$firstIndex,$lastIndex]');
// },
),
itemBuilder: (BuildContext c, int index) {
final Color color = getRandomColor(index);
return Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
color: getRandomColor(index)),
alignment: Alignment.center,
child: Text(
'$index ' + 'TestString' * 10 * (index % 3 + 1),
style: TextStyle(
color: color.computeLuminance() < 0.5
? Colors.white
: Colors.black),
),
//height: ((index % 3) + 1) * 100.0,
);
},
//itemCount: 19,
itemCount: length,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
// if (textDirection == TextDirection.ltr) {
// textDirection = TextDirection.rtl;
// } else {
// textDirection = TextDirection.ltr;
// }
//length=0;
crossAxisCount++;
//mainAxisSpacing += 5.0;
//crossAxisSpacing+=5.0;
});
},
child: const Icon(Icons.add),
),
);
}
Color getRandomColor(int index) {
if (index >= colors.length) {
colors.add(Color.fromARGB(255, Random.secure().nextInt(255),
Random.secure().nextInt(255), Random.secure().nextInt(255)));
}
return colors[index];
}
}
# Flutter-related
**/Flutter/ephemeral/
**/Pods/
# Xcode-related
**/xcuserdata/
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "ephemeral/Flutter-Generated.xcconfig"
//
// Generated file. Do not edit.
//
import FlutterMacOS
import Foundation
import path_provider_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
}
platform :osx, '10.11'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def parse_KV_file(file, separator='=')
file_abs_path = File.expand_path(file)
if !File.exists? file_abs_path
return [];
end
pods_ary = []
skip_line_start_symbols = ["#", "/"]
File.foreach(file_abs_path) { |line|
next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
plugin = line.split(pattern=separator)
if plugin.length == 2
podname = plugin[0].strip()
path = plugin[1].strip()
podpath = File.expand_path("#{path}", file_abs_path)
pods_ary.push({:name => podname, :path => podpath});
else
puts "Invalid plugin specification: #{line}"
end
}
return pods_ary
end
def pubspec_supports_macos(file)
file_abs_path = File.expand_path(file)
if !File.exists? file_abs_path
return false;
end
File.foreach(file_abs_path) { |line|
return true if line =~ /^\s*macos:/
}
return false
end
target 'Runner' do
use_frameworks!
use_modular_headers!
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
# referring to absolute paths on developers' machines.
ephemeral_dir = File.join('Flutter', 'ephemeral')
symlink_dir = File.join(ephemeral_dir, '.symlinks')
symlink_plugins_dir = File.join(symlink_dir, 'plugins')
system("rm -rf #{symlink_dir}")
system("mkdir -p #{symlink_plugins_dir}")
# Flutter Pods
generated_xcconfig = parse_KV_file(File.join(ephemeral_dir, 'Flutter-Generated.xcconfig'))
if generated_xcconfig.empty?
puts "Flutter-Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first."
end
generated_xcconfig.map { |p|
if p[:name] == 'FLUTTER_FRAMEWORK_DIR'
symlink = File.join(symlink_dir, 'flutter')
File.symlink(File.dirname(p[:path]), symlink)
pod 'FlutterMacOS', :path => File.join(symlink, File.basename(p[:path]))
end
}
# Plugin Pods
plugin_pods = parse_KV_file('../.flutter-plugins')
plugin_pods.map { |p|
symlink = File.join(symlink_plugins_dir, p[:name])
File.symlink(p[:path], symlink)
if pubspec_supports_macos(File.join(symlink, 'pubspec.yaml'))
pod p[:name], :path => File.join(symlink, 'macos')
end
}
end
# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system.
install! 'cocoapods', :disable_input_output_paths => true
PODS:
- FlutterMacOS (1.0.0)
- path_provider (0.0.1)
- path_provider_macos (0.0.1):
- FlutterMacOS
DEPENDENCIES:
- FlutterMacOS (from `Flutter/ephemeral/.symlinks/flutter/darwin-x64`)
- path_provider (from `Flutter/ephemeral/.symlinks/plugins/path_provider/macos`)
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
EXTERNAL SOURCES:
FlutterMacOS:
:path: Flutter/ephemeral/.symlinks/flutter/darwin-x64
path_provider:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider/macos
path_provider_macos:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
SPEC CHECKSUMS:
FlutterMacOS: 15bea8a44d2fa024068daa0140371c020b4b6ff9
path_provider: e0848572d1d38b9a7dd099e79cf83f5b7e2cde9f
path_provider_macos: a0a3fd666cb7cd0448e936fb4abad4052961002b
PODFILE CHECKSUM: d8ba9b3e9e93c62c74a660b46c6fcb09f03991a7
COCOAPODS: 1.9.3
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objects = {
/* Begin PBXAggregateTarget section */
33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
isa = PBXAggregateTarget;
buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
buildPhases = (
33CC111E2044C6BF0003C045 /* ShellScript */,
);
dependencies = (
);
name = "Flutter Assemble";
productName = FLX;
};
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; };
33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = 33D1A10322148B71006C7A3E /* FlutterMacOS.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
5418B21A951BB5A69941CC01 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0CB9E840061A2185D4F3FF80 /* Pods_Runner.framework */; };
D73912F022F37F9E000D13A0 /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D73912EF22F37F9E000D13A0 /* App.framework */; };
D73912F222F3801D000D13A0 /* App.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = D73912EF22F37F9E000D13A0 /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 33CC111A2044C6BA0003C045;
remoteInfo = FLX;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
33CC110E2044A8840003C045 /* Bundle Framework */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
D73912F222F3801D000D13A0 /* App.framework in Bundle Framework */,
33D1A10522148B93006C7A3E /* FlutterMacOS.framework in Bundle Framework */,
);
name = "Bundle Framework";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
0CB9E840061A2185D4F3FF80 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
2CAEAF66C25BB426870E3329 /* 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 = "<group>"; };
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = "<group>"; };
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = "<group>"; };
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = "<group>"; };
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = "<group>"; };
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = "<group>"; };
33D1A10322148B71006C7A3E /* FlutterMacOS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FlutterMacOS.framework; path = Flutter/ephemeral/FlutterMacOS.framework; sourceTree = SOURCE_ROOT; };
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
39458616FA7CAA5A684D151C /* 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 = "<group>"; };
4FE541B9FF3AD544B9096546 /* 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 = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
D73912EF22F37F9E000D13A0 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/ephemeral/App.framework; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
33CC10EA2044A3C60003C045 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D73912F022F37F9E000D13A0 /* App.framework in Frameworks */,
33D1A10422148B71006C7A3E /* FlutterMacOS.framework in Frameworks */,
5418B21A951BB5A69941CC01 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
26C1D6710B9F7CBA3EA7A12D /* Pods */ = {
isa = PBXGroup;
children = (
39458616FA7CAA5A684D151C /* Pods-Runner.debug.xcconfig */,
4FE541B9FF3AD544B9096546 /* Pods-Runner.release.xcconfig */,
2CAEAF66C25BB426870E3329 /* Pods-Runner.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
33BA886A226E78AF003329D5 /* Configs */ = {
isa = PBXGroup;
children = (
33E5194F232828860026EE4D /* AppInfo.xcconfig */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
);
path = Configs;
sourceTree = "<group>";
};
33CC10E42044A3C60003C045 = {
isa = PBXGroup;
children = (
33FAB671232836740065AC1E /* Runner */,
33CEB47122A05771004F2AC0 /* Flutter */,
33CC10EE2044A3C60003C045 /* Products */,
D73912EC22F37F3D000D13A0 /* Frameworks */,
26C1D6710B9F7CBA3EA7A12D /* Pods */,
);
sourceTree = "<group>";
};
33CC10EE2044A3C60003C045 /* Products */ = {
isa = PBXGroup;
children = (
33CC10ED2044A3C60003C045 /* example.app */,
);
name = Products;
sourceTree = "<group>";
};
33CC11242044D66E0003C045 /* Resources */ = {
isa = PBXGroup;
children = (
33CC10F22044A3C60003C045 /* Assets.xcassets */,
33CC10F42044A3C60003C045 /* MainMenu.xib */,
33CC10F72044A3C60003C045 /* Info.plist */,
);
name = Resources;
path = ..;
sourceTree = "<group>";
};
33CEB47122A05771004F2AC0 /* Flutter */ = {
isa = PBXGroup;
children = (
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
D73912EF22F37F9E000D13A0 /* App.framework */,
33D1A10322148B71006C7A3E /* FlutterMacOS.framework */,
);
path = Flutter;
sourceTree = "<group>";
};
33FAB671232836740065AC1E /* Runner */ = {
isa = PBXGroup;
children = (
33CC10F02044A3C60003C045 /* AppDelegate.swift */,
33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
33E51913231747F40026EE4D /* DebugProfile.entitlements */,
33E51914231749380026EE4D /* Release.entitlements */,
33CC11242044D66E0003C045 /* Resources */,
33BA886A226E78AF003329D5 /* Configs */,
);
path = Runner;
sourceTree = "<group>";
};
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
isa = PBXGroup;
children = (
0CB9E840061A2185D4F3FF80 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
33CC10EC2044A3C60003C045 /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
5AB4BCFF93F692834DBCD171 /* [CP] Check Pods Manifest.lock */,
33CC10E92044A3C60003C045 /* Sources */,
33CC10EA2044A3C60003C045 /* Frameworks */,
33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */,
76CA9536FF591539E2792178 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
33CC11202044C79F0003C045 /* PBXTargetDependency */,
);
name = Runner;
productName = Runner;
productReference = 33CC10ED2044A3C60003C045 /* example.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
33CC10E52044A3C60003C045 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 0930;
ORGANIZATIONNAME = "The Flutter Authors";
TargetAttributes = {
33CC10EC2044A3C60003C045 = {
CreatedOnToolsVersion = 9.2;
LastSwiftMigration = 1100;
ProvisioningStyle = Automatic;
SystemCapabilities = {
com.apple.Sandbox = {
enabled = 1;
};
};
};
33CC111A2044C6BA0003C045 = {
CreatedOnToolsVersion = 9.2;
ProvisioningStyle = Manual;
};
};
};
buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 8.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 33CC10E42044A3C60003C045;
productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
33CC10EC2044A3C60003C045 /* Runner */,
33CC111A2044C6BA0003C045 /* Flutter Assemble */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
33CC10EB2044A3C60003C045 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3399D490228B24CF009A79C7 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename\n";
};
33CC111E2044C6BF0003C045 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
Flutter/ephemeral/FlutterInputs.xcfilelist,
);
inputPaths = (
Flutter/ephemeral/tripwire,
);
outputFileListPaths = (
Flutter/ephemeral/FlutterOutputs.xcfilelist,
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
};
5AB4BCFF93F692834DBCD171 /* [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;
};
76CA9536FF591539E2792178 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
33CC10E92044A3C60003C045 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
33CC10F52044A3C60003C045 /* Base */,
);
name = MainMenu.xib;
path = Runner;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
338D0CE9231458BD00FA5F75 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
};
name = Profile;
};
338D0CEA231458BD00FA5F75 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter/ephemeral",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
name = Profile;
};
338D0CEB231458BD00FA5F75 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Profile;
};
33CC10F92044A3C60003C045 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
33CC10FA2044A3C60003C045 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
};
name = Release;
};
33CC10FC2044A3C60003C045 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter/ephemeral",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
33CC10FD2044A3C60003C045 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter/ephemeral",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
};
name = Release;
};
33CC111C2044C6BA0003C045 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
33CC111D2044C6BA0003C045 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
33CC10F92044A3C60003C045 /* Debug */,
33CC10FA2044A3C60003C045 /* Release */,
338D0CE9231458BD00FA5F75 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
33CC10FC2044A3C60003C045 /* Debug */,
33CC10FD2044A3C60003C045 /* Release */,
338D0CEA231458BD00FA5F75 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
isa = XCConfigurationList;
buildConfigurations = (
33CC111C2044C6BA0003C045 /* Debug */,
33CC111D2044C6BA0003C045 /* Release */,
338D0CEB231458BD00FA5F75 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 33CC10E52044A3C60003C045 /* Project object */;
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1000"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "example.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00380F9121DF178D00097171"
BuildableName = "RunnerUITests.xctest"
BlueprintName = "RunnerUITests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "example.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "example.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
BuildableName = "example.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
import Cocoa
import FlutterMacOS
@NSApplicationMain
class AppDelegate: FlutterAppDelegate {
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
}
{
"images" : [
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "app_icon_16.png",
"scale" : "1x"
},
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "app_icon_32.png",
"scale" : "2x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "app_icon_32.png",
"scale" : "1x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "app_icon_64.png",
"scale" : "2x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "app_icon_128.png",
"scale" : "1x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "app_icon_256.png",
"scale" : "2x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "app_icon_256.png",
"scale" : "1x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "app_icon_512.png",
"scale" : "2x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "app_icon_512.png",
"scale" : "1x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "app_icon_1024.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Runner" customModuleProvider="target">
<connections>
<outlet property="applicationMenu" destination="uQy-DD-JDr" id="XBo-yE-nKs"/>
<outlet property="mainFlutterWindow" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="APP_NAME" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="APP_NAME" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About APP_NAME" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide APP_NAME" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit APP_NAME" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
</connections>
</menuItem>
<menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
<connections>
<action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
<connections>
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
<connections>
<action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
<connections>
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO">
<items>
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
<point key="canvasLocation" x="142" y="-258"/>
</menu>
<window title="APP_NAME" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g" customClass="MainFlutterWindow" customModule="Runner" customModuleProvider="target">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<rect key="contentRect" x="335" y="390" width="800" height="600"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1577"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="800" height="600"/>
<autoresizingMask key="autoresizingMask"/>
</view>
</window>
</objects>
</document>
// Application-level settings for the Runner target.
//
// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
// future. If not, the values below would default to using the project name when this becomes a
// 'flutter create' template.
// The application's name. By default this is also the title of the Flutter window.
PRODUCT_NAME = example
// The application's bundle identifier
PRODUCT_BUNDLE_IDENTIFIER = com.zmtzawqlp.example
// The copyright displayed in application information
PRODUCT_COPYRIGHT = Copyright © 2020 com.zmtzawqlp. All rights reserved.
#include "../../Flutter/Flutter-Debug.xcconfig"
#include "Warnings.xcconfig"
#include "../../Flutter/Flutter-Release.xcconfig"
#include "Warnings.xcconfig"
WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
GCC_WARN_UNDECLARED_SELECTOR = YES
CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
CLANG_WARN_PRAGMA_PACK = YES
CLANG_WARN_STRICT_PROTOTYPES = YES
CLANG_WARN_COMMA = YES
GCC_WARN_STRICT_SELECTOR_MATCH = YES
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
GCC_WARN_SHADOW = YES
CLANG_WARN_UNREACHABLE_CODE = YES
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>$(PRODUCT_COPYRIGHT)</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
import Cocoa
import FlutterMacOS
class MainFlutterWindow: NSWindow {
override func awakeFromNib() {
let flutterViewController = FlutterViewController.init()
let windowFrame = self.frame
self.contentViewController = flutterViewController
self.setFrame(windowFrame, display: true)
RegisterGeneratedPlugins(registry: flutterViewController)
super.awakeFromNib()
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>
name: example
description: A new Flutter project.
version: 1.0.0+1
environment:
sdk: ">=2.6.0 <3.0.0"
flutter: ">=1.22.4"
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.2
url_launcher: 5.3.0
http_client_helper: any
extended_image: any
pull_to_refresh_notification: any
like_button: any
extended_sliver: ^1.0.1
waterfall_flow:
path: ../
flutter_sliver_tracker: ^1.0.0
dev_dependencies:
flutter_test:
sdk: flutter
ff_annotation_route: ^4.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
assets:
# assets start
# GENERATED CODE - DO NOT MODIFY MANUALLY
# **************************************************************************
# Auto generated by https://github.com/fluttercandies/assets_generator
# **************************************************************************
- assets/
# assets end
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
\ No newline at end of file
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. 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:example/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(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);
});
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="example">
<link rel="apple-touch-icon" href="/icons/Icon-192.png">
<title>example</title>
<link rel="manifest" href="/manifest.json">
</head>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('/flutter_service_worker.js');
});
}
</script>
<script src="main.dart.js" type="application/javascript"></script>
</body>
</html>
{
"name": "example",
"short_name": "example",
"start_url": ".",
"display": "minimal-ui",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "A new Flutter project.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
import 'package:flutter/rendering.dart';
import 'typedef.dart';
enum LastChildLayoutType {
/// as default child
none,
/// follow max child trailing layout offset and layout with full cross-axis extent
/// last child as load more item/no more item in [ExtendedGridView] and [WaterfallFlow]
/// with full cross axis extend
fullCrossAxisExtent,
/// as foot at trailing and layout with full cross axis extend
/// show no more item at trailing when children are not full of viewport
/// if children is full of viewport, it's the same as fullCrossAxisExtent
foot,
}
/// A delegate that provides extensions within the [ExtendedGridView],[ExtendedList],[WaterfallFlow].
class ExtendedListDelegate {
const ExtendedListDelegate({
this.lastChildLayoutTypeBuilder,
this.collectGarbage,
this.viewportBuilder,
this.closeToTrailing = false,
}) : assert(
closeToTrailing != null,
);
/// The builder to get layout type of last child
/// Notice: it should only for last child
final LastChildLayoutTypeBuilder lastChildLayoutTypeBuilder;
/// Call when collect garbage, return indexes of children which are disposed to collect
final CollectGarbage collectGarbage;
/// The builder to get indexes in viewport
final ViewportBuilder viewportBuilder;
/// when reverse property of List is true, layout is as following.
/// it likes chat list, and new session will insert to zero index
/// but it's not right when items are not full of viewport.
///
/// trailing
/// -----------------
/// | |
/// | |
/// | item2 |
/// | item1 |
/// | item0 |
/// -----------------
/// leading
///
/// to solve it, you could set closeToTrailing to true, layout is as following.
/// support [ExtendedGridView],[ExtendedList],[WaterfallFlow]
/// it works not only reverse is true.
///
/// trailing
/// -----------------
/// | item2 |
/// | item1 |
/// | item0 |
/// | |
/// | |
/// -----------------
/// leading
///
final bool closeToTrailing;
}
/// mixin of extended list render
/// if sliver is all out of viewport then return [-1,-1] or nothing
mixin ExtendedRenderObjectMixin on RenderSliverMultiBoxAdaptor {
/// call ViewportBuilder if it's not null
void callViewportBuilder({
ViewportBuilder viewportBuilder,
//ExtentList and GridView can't use paintExtentOf
PaintExtentOf getPaintExtend,
double mainAxisSpacing,
}) {
if (viewportBuilder == null) {
return;
}
/// it's not go into viewport
if (firstChild == null ||
//sometime, remainingPaintExtent is not zero though sliver is not go into viewport
//maybe this is issue for viewport
(constraints.precedingScrollExtent != 0.0 && constraints.remainingPaintExtent == 0)) {
return;
}
int viewportFirstIndex = -1;
int viewportLastIndex = -1;
mainAxisSpacing ??= 0;
RenderBox viewportFirstChild = firstChild;
while (true) {
final double layoutOffset = childScrollOffset(viewportFirstChild);
final double heightOffset = getPaintExtend != null ? getPaintExtend(viewportFirstChild) : paintExtentOf(viewportFirstChild);// 固定120高度
final double trailingOffset = layoutOffset + heightOffset;
final double _scrollOffset = constraints.scrollOffset;
if (_scrollOffset < trailingOffset) {//layoutOffset - (layoutOffset == 0 ? 0 : mainAxisSpacing) <= _scrollOffset &&
final double scale = (trailingOffset - _scrollOffset) / heightOffset;
viewportFirstIndex = indexOf(viewportFirstChild);
if(scale>=0.8){
break;
}
}
viewportFirstChild = childAfter(viewportFirstChild);
if (viewportFirstChild == null) {
break;
}
}
RenderBox viewportLastChild = lastChild;
while (true) {
final double layoutOffset = childScrollOffset(viewportLastChild);
final double heightOffset = getPaintExtend != null ? getPaintExtend(viewportLastChild) : paintExtentOf(viewportLastChild);
final double trailingOffset = layoutOffset + heightOffset;
if (layoutOffset < constraints.scrollOffset + constraints.remainingPaintExtent && trailingOffset >= constraints.scrollOffset) {
final double scale = (constraints.scrollOffset + constraints.remainingPaintExtent - layoutOffset) / heightOffset;
viewportLastIndex = indexOf(viewportLastChild);
if(scale>=0.8){
break;
}
}
viewportLastChild = childBefore(viewportLastChild);
if (viewportLastChild == null) {
break;
}
}
viewportBuilder(viewportFirstIndex, viewportLastIndex);
}
/// call CollectGarbage if it's not null
void callCollectGarbage({
CollectGarbage collectGarbage,
int leadingGarbage,
int trailingGarbage,
int firstIndex,
int targetLastIndex,
}) {
if (collectGarbage == null) {
return;
}
final List<int> garbages = <int>[];
firstIndex ??= indexOf(firstChild);
targetLastIndex ??= indexOf(lastChild);
for (int i = leadingGarbage; i > 0; i--) {
garbages.add(firstIndex - i);
}
for (int i = 0; i < trailingGarbage; i++) {
garbages.add(targetLastIndex + i);
}
if (garbages.isNotEmpty) {
//call collectGarbage
collectGarbage.call(garbages);
}
}
/// handle closeToTrailing at begin
void handleCloseToTrailingBegin(bool closeToTrailing) {
_closeToTrailingDistance = null;
// if (closeToTrailing) {
// RenderBox child = firstChild;
// SliverMultiBoxAdaptorParentData childParentData =
// child.parentData as SliverMultiBoxAdaptorParentData;
// if (childParentData.index == 0 && childParentData.layoutOffset != 0) {
// final double distance = childParentData.layoutOffset;
// while (child != null) {
// childParentData = child.parentData as SliverMultiBoxAdaptorParentData;
// childParentData.layoutOffset -= distance;
// child = childAfter(child);
// }
// }
// }
}
/// handle closeToTrailing at end
double handleCloseToTrailingEnd(
bool closeToTrailing, double endScrollOffset) {
if (closeToTrailing && endScrollOffset < constraints.remainingPaintExtent) {
//RenderBox child = firstChild;
final double distance =
constraints.remainingPaintExtent - endScrollOffset;
_closeToTrailingDistance = distance;
// while (child != null) {
// final SliverMultiBoxAdaptorParentData childParentData =
// child.parentData as SliverMultiBoxAdaptorParentData;
// childParentData.layoutOffset += distance;
// child = childAfter(child);
// }
return constraints.remainingPaintExtent;
}
return endScrollOffset;
}
double _closeToTrailingDistance;
double get closeToTrailingDistance => _closeToTrailingDistance ?? 0.0;
bool get closeToTrailing => extendedListDelegate?.closeToTrailing ?? false;
ExtendedListDelegate get extendedListDelegate;
@override
double childScrollOffset(RenderObject child) {
assert(child != null);
assert(child.parent == this);
final SliverMultiBoxAdaptorParentData childParentData =
child.parentData as SliverMultiBoxAdaptorParentData;
return childParentData.layoutOffset + closeToTrailingDistance;
}
}
import 'package:flutter/rendering.dart';
import 'extended_list_library.dart';
/// The builder to get layout type of last child
/// Notice: it should only for last child
typedef LastChildLayoutTypeBuilder = LastChildLayoutType Function(int index);
/// Return indexes of children which are disposed to collect
typedef CollectGarbage = void Function(List<int> garbages);
///
/// 滑动过程中展示的索引
/// if sliver is all out of viewport then return [-1,-1]
///
typedef ViewportBuilder = void Function(int firstIndex, int lastIndex);
/// Return paint extent of child
typedef PaintExtentOf = double Function(RenderBox child);
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:waterfall_flow/src/extended/typedef.dart';
import '../../waterfall_flow.dart';
///
/// create by zmtzawqlp on 2019/11/9
///
/// Controls the masonry layout of tiles.
///
/// Given the current constraints on the grid, a [SliverGridDelegate] computes
/// the masonry layout for the tiles in the grid. The tiles can be placed as masonry
/// layout with equally cross-axis sized and spaced. A contiguous sequence of children are
/// laid out after the shortest one of the previous row/column。
///
/// See also:
///
/// * [SliverWaterfallFlowDelegateWithFixedCrossAxisCount], which creates a masonry
/// layout with a fixed number of tiles in the cross axis.
/// * [SliverWaterfallFlowDelegateWithMaxCrossAxisExtent], which creates a masonry
/// layout that have a maximum cross-axis extent.
/// * [GridMasonryView], which uses this delegate to control the layout of its tiles.
/// * [SliverWaterfallFlow], which uses this delegate to control the layout of its
/// tiles.
/// * [RenderSliverWaterfallFlow], which uses this delegate to control the layout of its
/// tiles.
abstract class SliverWaterfallFlowDelegate extends ExtendedListDelegate {
/// Creates a delegate that makes masonry layout with tiles in
/// the cross axis.
///
/// All of the arguments must not be null. The `mainAxisSpacing` and
/// `crossAxisSpacing` arguments must not be negative.
const SliverWaterfallFlowDelegate({
this.mainAxisSpacing = 0.0,
this.crossAxisSpacing = 0.0,
LastChildLayoutTypeBuilder lastChildLayoutTypeBuilder,
CollectGarbage collectGarbage,
ViewportBuilder viewportBuilder,
bool closeToTrailing = false,
}) : assert(mainAxisSpacing != null && mainAxisSpacing >= 0),
assert(crossAxisSpacing != null && crossAxisSpacing >= 0),
super(
lastChildLayoutTypeBuilder: lastChildLayoutTypeBuilder,
collectGarbage: collectGarbage,
viewportBuilder: viewportBuilder,
closeToTrailing: closeToTrailing,
);
/// The number of logical pixels between each child along the main axis.
final double mainAxisSpacing;
/// The number of logical pixels between each child along the cross axis.
final double crossAxisSpacing;
/// Return the offset of the child in the non-scrolling axis.
double getCrossAxisOffset(SliverConstraints constraints, int crossAxisIndex) {
final bool reverseCrossAxis =
axisDirectionIsReversed(constraints.crossAxisDirection);
final int crossAxisCount = getCrossAxisCount(constraints);
final double childUsableCrossAxisExtent =
getChildUsableCrossAxisExtent(constraints);
final int actualCrossAxisIndex = (reverseCrossAxis && crossAxisCount > 1)
? crossAxisCount - 1 - crossAxisIndex
: crossAxisIndex;
return actualCrossAxisIndex %
crossAxisCount *
(childUsableCrossAxisExtent + crossAxisSpacing);
}
/// Return usable cross-axis extent of each child.
///
/// It doesn't contain [crossAxisSpacing].
double getChildUsableCrossAxisExtent(SliverConstraints constraints) {
final int crossAxisCount = getCrossAxisCount(constraints);
final double usableCrossAxisExtent = max(0.0,
constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1));
return usableCrossAxisExtent / crossAxisCount;
}
/// Return [crossAxisCount] by [SliverWaterfallFlowDelegateWithFixedCrossAxisCount]
/// and [SliverWaterfallFlowDelegateWithMaxCrossAxisExtent].
int getCrossAxisCount(SliverConstraints constraints);
/// Return true when the children need to be laid out.
///
/// This should compare the fields of the current delegate and the given
/// `oldDelegate` and return true if the fields are such that the layout would
/// be different.
bool shouldRelayout(SliverWaterfallFlowDelegate oldDelegate) {
return oldDelegate.mainAxisSpacing != mainAxisSpacing ||
oldDelegate.crossAxisSpacing != crossAxisSpacing ||
oldDelegate.closeToTrailing != closeToTrailing;
}
LastChildLayoutType getLastChildLayoutType(int index) {
if (lastChildLayoutTypeBuilder == null) {
return LastChildLayoutType.none;
}
return lastChildLayoutTypeBuilder(index) ?? LastChildLayoutType.none;
}
}
/// Creates masonry layouts with a fixed number of tiles in the cross axis.
///
/// For example, if the grid is vertical, this delegate will create a layout
/// with a fixed number of columns. If the grid is horizontal, this delegate
/// will create a layout with a fixed number of rows.
///
/// This delegate creates grids with equally cross-axis sized and spaced tiles.
///
/// See also:
///
/// * [SliverWaterfallFlowDelegateWithMaxCrossAxisExtent], which creates a masonry
/// layout that have a maximum cross-axis extent.
/// * [MasonryGridView], which uses this delegate to control the layout of its tiles.
/// * [SliverWaterfallFlow], which uses this delegate to control the layout of its
/// tiles.
/// * [RenderSliverWaterfallFlow], which uses this delegate to control the layout of its
/// tiles.
class SliverWaterfallFlowDelegateWithFixedCrossAxisCount
extends SliverWaterfallFlowDelegate {
/// Creates a delegate that makes masonry layouts with a fixed number of tiles in
/// the cross axis.
///
/// All of the arguments must not be null. The `mainAxisSpacing` and
/// `crossAxisSpacing` arguments must not be negative.The `crossAxisCount`
/// must be greater than zero.
const SliverWaterfallFlowDelegateWithFixedCrossAxisCount({
@required this.crossAxisCount,
double mainAxisSpacing = 0.0,
double crossAxisSpacing = 0.0,
LastChildLayoutTypeBuilder lastChildLayoutTypeBuilder,
CollectGarbage collectGarbage,
ViewportBuilder viewportBuilder,
bool closeToTrailing = false,
}) : assert(crossAxisCount != null && crossAxisCount > 0),
super(
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
lastChildLayoutTypeBuilder: lastChildLayoutTypeBuilder,
collectGarbage: collectGarbage,
viewportBuilder: viewportBuilder,
closeToTrailing: closeToTrailing,
);
/// The number of children in the cross axis.
final int crossAxisCount;
@override
int getCrossAxisCount(SliverConstraints constraints) {
return crossAxisCount;
}
@override
bool shouldRelayout(SliverWaterfallFlowDelegate oldDelegate) {
if (oldDelegate.runtimeType != runtimeType) {
return true;
}
return oldDelegate is SliverWaterfallFlowDelegateWithFixedCrossAxisCount &&
(oldDelegate.crossAxisCount != crossAxisCount ||
super.shouldRelayout(oldDelegate));
}
}
/// Creates masonry layouts with tiles that each have a maximum cross-axis extent.
///
/// This delegate will select a cross-axis extent for the tiles that is as
/// large as possible subject to the following conditions:
///
/// - The extent evenly divides the cross-axis extent of the grid.
/// - The extent is at most [maxCrossAxisExtent].
///
/// For example, if the grid is vertical, the grid is 500.0 pixels wide, and
/// [maxCrossAxisExtent] is 150.0, this delegate will create a grid with 4
/// columns that are 125.0 pixels wide.
///
/// This delegate creates grids with equally cross-axis sized and spaced tiles.
///
/// See also:
///
/// * [SliverWaterfallFlowDelegateWithFixedCrossAxisCount], which creates a masonry
/// layout with a fixed number of tiles in the cross axis.
/// * [MasonryGridView], which uses this delegate to control the layout of its tiles.
/// * [SliverWaterfallFlow], which uses this delegate to control the layout of its
/// tiles.
/// * [RenderSliverWaterfallFlow], which uses this delegate to control the layout of its
/// tiles.
class SliverWaterfallFlowDelegateWithMaxCrossAxisExtent
extends SliverWaterfallFlowDelegate {
/// Creates a delegate that makes masonry layouts with tiles that have a maximum
/// cross-axis extent.
///
/// All of the arguments must not be null. The [maxCrossAxisExtent],
/// [mainAxisSpacing], and [crossAxisSpacing] arguments must not be negative.
const SliverWaterfallFlowDelegateWithMaxCrossAxisExtent({
@required this.maxCrossAxisExtent,
double mainAxisSpacing = 0.0,
double crossAxisSpacing = 0.0,
LastChildLayoutTypeBuilder lastChildLayoutTypeBuilder,
CollectGarbage collectGarbage,
ViewportBuilder viewportBuilder,
bool closeToTrailing = false,
}) : assert(maxCrossAxisExtent != null && maxCrossAxisExtent >= 0),
super(
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
lastChildLayoutTypeBuilder: lastChildLayoutTypeBuilder,
collectGarbage: collectGarbage,
viewportBuilder: viewportBuilder,
closeToTrailing: closeToTrailing,
);
/// The maximum extent of tiles in the cross axis.
///
/// This delegate will select a cross-axis extent for the tiles that is as
/// large as possible subject to the following conditions:
///
/// - The extent evenly divides the cross-axis extent of the grid.
/// - The extent is at most [maxCrossAxisExtent].
///
/// For example, if the grid is vertical, the grid is 500.0 pixels wide, and
/// [maxCrossAxisExtent] is 150.0, this delegate will create a grid with 4
/// columns that are 125.0 pixels wide.
final double maxCrossAxisExtent;
@override
int getCrossAxisCount(SliverConstraints constraints) {
return (constraints.crossAxisExtent /
(maxCrossAxisExtent + crossAxisSpacing))
.ceil();
}
@override
bool shouldRelayout(SliverWaterfallFlowDelegate oldDelegate) {
if (oldDelegate.runtimeType != runtimeType) {
return true;
}
return oldDelegate is SliverWaterfallFlowDelegateWithMaxCrossAxisExtent &&
(oldDelegate.maxCrossAxisExtent != maxCrossAxisExtent ||
super.shouldRelayout(oldDelegate));
}
}
/// Parent data structure used by [RenderSliverWaterfallFlow].
class SliverWaterfallFlowParentData extends SliverMultiBoxAdaptorParentData {
/// The trailing position of the child relative to the zero scroll offset.
///
/// The number of pixels from from the zero scroll offset of the parent sliver
/// (the line at which its [SliverConstraints.scrollOffset] is zero) to the
/// side of the child closest to that offset.
///
/// In a typical list, this does not change as the parent is scrolled.
double trailingLayoutOffset;
/// The index of crossAxis.
int crossAxisIndex;
/// The offset of the child in the non-scrolling axis.
///
/// If the scroll axis is vertical, this offset is from the left-most edge of
/// the parent to the left-most edge of the child. If the scroll axis is
/// horizontal, this offset is from the top-most edge of the parent to the
/// top-most edge of the child.
double crossAxisOffset;
/// The indexes of the children in the same index of crossAxis.
List<int> indexes = <int>[];
@override
String toString() =>
'crossAxisIndex=$crossAxisIndex;crossAxisOffset=$crossAxisOffset;trailingLayoutOffset=$trailingLayoutOffset;indexes$indexes; ${super.toString()}';
}
/// A sliver that places multiple box children with masonry layouts.
///
/// [RenderSliverWaterfallFlow] places its children in arbitrary positions determined by
/// [gridDelegate]. Each child is forced to have the cross-axis size specified by the
/// [gridDelegate].
///
/// See also:
///
/// * [RenderSliverList], which places its children in a linear
/// array.
/// * [RenderSliverFixedExtentList], which places its children in a linear
/// array with a fixed extent in the main axis.
/// * [RenderSliverGrid], which places its children in arbitrary positions.
class RenderSliverWaterfallFlow extends RenderSliverMultiBoxAdaptor
with ExtendedRenderObjectMixin {
/// Creates a sliver that contains multiple box children that whose cross-axis size
/// is determined by a delegate and position is determined by masonry rule.
///
/// The [childManager] and [gridDelegate] arguments must not be null.
RenderSliverWaterfallFlow({
@required RenderSliverBoxChildManager childManager,
@required SliverWaterfallFlowDelegate gridDelegate,
}) : assert(gridDelegate != null),
_gridDelegate = gridDelegate,
super(childManager: childManager);
/// It stores parent data of the leading and trailing.
_CrossAxisChildrenData _previousCrossAxisChildrenData;
/// The delegate that controls the size and position of the children.
SliverWaterfallFlowDelegate get gridDelegate => _gridDelegate;
SliverWaterfallFlowDelegate _gridDelegate;
set gridDelegate(SliverWaterfallFlowDelegate value) {
assert(value != null);
if (_gridDelegate == value) {
return;
}
if (value.shouldRelayout(_gridDelegate)) {
markNeedsLayout();
}
_gridDelegate = value;
}
@override
ExtendedListDelegate get extendedListDelegate => _gridDelegate;
@override
void setupParentData(RenderObject child) {
if (child.parentData is! SliverWaterfallFlowParentData)
child.parentData = SliverWaterfallFlowParentData();
}
@override
double childCrossAxisPosition(RenderBox child) {
final SliverWaterfallFlowParentData childParentData =
child.parentData as SliverWaterfallFlowParentData;
return childParentData.crossAxisOffset;
}
@override
void performLayout() {
childManager.didStartLayout();
childManager.setDidUnderflow(false);
_clearIfNeed();
final _CrossAxisChildrenData crossAxisChildrenData = _CrossAxisChildrenData(
gridDelegate: _gridDelegate,
constraints: constraints,
leadingChildren: _previousCrossAxisChildrenData?.leadingChildren,
);
final BoxConstraints childConstraints = constraints.asBoxConstraints(
crossAxisExtent:
_gridDelegate.getChildUsableCrossAxisExtent(constraints));
final double scrollOffset =
constraints.scrollOffset + constraints.cacheOrigin;
assert(scrollOffset >= 0.0);
final double remainingExtent = constraints.remainingCacheExtent;
assert(remainingExtent >= 0.0);
final double targetEndScrollOffset = scrollOffset + remainingExtent;
int leadingGarbage = 0;
int trailingGarbage = 0;
bool reachedEnd = false;
// This algorithm in principle is straight-forward: find the leading children
// that overlaps the given scrollOffset base on [_previousCrossAxisChildrenData],
// creating more children after the shortest one of the previous row/column,
// then walk down the list updating and laying out
// each child and adding more at the end if necessary until we have enough
// children to cover the entire viewport.
//
// It is complicated by one minor issue, which is that any time you update
// or create a child, it's possible that the some of the children that
// haven't yet been laid out will be removed, leaving the list in an
// inconsistent state, and requiring that missing nodes be recreated.
//
// To keep this mess tractable, this algorithm starts from what is currently
// the first child, if any, and then walks up and/or down from there, so
// that the nodes that might get removed are always at the edges of what has
// already been laid out.
// Make sure we have at least one child to start from.
if (firstChild == null) {
if (!addInitialChild()) {
// There are no children.
geometry = SliverGeometry.zero;
_previousCrossAxisChildrenData = null;
childManager.didFinishLayout();
return;
}
}
// zmt
handleCloseToTrailingBegin(_gridDelegate?.closeToTrailing ?? false);
// In case,the itemCount is changed, clear all,
final SliverWaterfallFlowParentData firstChildParentData =
firstChild.parentData as SliverWaterfallFlowParentData;
// In case of the itemCount is changed, clear all leading children,
// avoid calculate with dirty leading children.
if (firstChildParentData.index == 0) {
crossAxisChildrenData.clear();
}
// We have at least one child.
// These variables track the range of children that we have laid out. Within
// this range, the children have consecutive indices. Outside this range,
// it's possible for a child to get removed without notice.
RenderBox leadingChildWithLayout, trailingChildWithLayout;
// Find the last child that is at or before the scrollOffset.
RenderBox earliestUsefulChild = firstChild;
// A firstChild with null layout offset is likely a result of children
// reordering.
//
// We rely on firstChild to have accurate layout offset. In the case of null
// layout offset, we have to find the first child that has valid layout
// offset.
if (childScrollOffset(firstChild) == null) {
int leadingChildrenWithoutLayoutOffset = 0;
while (childScrollOffset(earliestUsefulChild) == null) {
earliestUsefulChild = childAfter(firstChild);
leadingChildrenWithoutLayoutOffset += 1;
}
// We should be able to destroy children with null layout offset safely,
// because they are likely outside of viewport
collectGarbage(leadingChildrenWithoutLayoutOffset, 0);
assert(firstChild != null);
}
// Find the last child that is at or before the scrollOffset.
earliestUsefulChild = firstChild;
if (crossAxisChildrenData.maxLeadingLayoutOffset > scrollOffset) {
RenderBox child = firstChild;
// Add children from min index to max index of leading to
// make sure indexes are continuous.
final int maxLeadingIndex = crossAxisChildrenData.maxLeadingIndex;
while (child != null && maxLeadingIndex > indexOf(child)) {
child = childAfter(child);
}
final int minLeadingIndex = crossAxisChildrenData.minLeadingIndex;
while (child != null && minLeadingIndex < indexOf(child)) {
crossAxisChildrenData.insertLeading(
child: child, paintExtentOf: paintExtentOf);
child = childBefore(child);
}
while (crossAxisChildrenData.maxLeadingLayoutOffset > scrollOffset) {
// We have to add children before the earliestUsefulChild.
earliestUsefulChild =
insertAndLayoutLeadingChild(childConstraints, parentUsesSize: true);
if (earliestUsefulChild == null) {
final SliverWaterfallFlowParentData data =
firstChild.parentData as SliverWaterfallFlowParentData;
assert(data.index == 0);
// In case of some child is changed small and we ran out of children
// before reaching the scroll offset.
// We must clear data, avoid calculate with dirty leading children.
firstChild.layout(childConstraints, parentUsesSize: true);
earliestUsefulChild = firstChild;
leadingChildWithLayout = earliestUsefulChild;
trailingChildWithLayout ??= earliestUsefulChild;
crossAxisChildrenData.clear();
break;
}
crossAxisChildrenData.insertLeading(
child: earliestUsefulChild, paintExtentOf: paintExtentOf);
final SliverWaterfallFlowParentData data =
earliestUsefulChild.parentData as SliverWaterfallFlowParentData;
// item after leadings
if (data.layoutOffset == null) {
continue;
}
// firstChildScrollOffset may contain double precision error
if (data.layoutOffset < -precisionErrorTolerance) {
// The first child doesn't fit within the viewport (underflow) and
// there may be additional children above it. Find the real first child
// and then correct the scroll position so that there's room for all and
// so that the trailing edge of the original firstChild appears where it
// was before the scroll offset correction.
// do this work incrementally, instead of all at once,
// find first child and clear all,
// avoid calculate with dirty leading children.
while (earliestUsefulChild != null) {
assert(firstChild == earliestUsefulChild);
earliestUsefulChild = insertAndLayoutLeadingChild(childConstraints,
parentUsesSize: true);
}
final SliverWaterfallFlowParentData data =
firstChild.parentData as SliverWaterfallFlowParentData;
assert(data.index == 0);
crossAxisChildrenData.clear();
earliestUsefulChild = firstChild;
}
assert(earliestUsefulChild == firstChild);
leadingChildWithLayout = earliestUsefulChild;
trailingChildWithLayout ??= earliestUsefulChild;
}
}
// At this point, earliestUsefulChild is the first child, and is a child
// whose scrollOffset is at or before the scrollOffset, and
// leadingChildWithLayout and trailingChildWithLayout are either null or
// cover a range of render boxes that we have laid out with the first being
// the same as earliestUsefulChild and the last being either at or after the
// scroll offset.
assert(earliestUsefulChild == firstChild);
// Make sure we've laid out at least one child.
if (leadingChildWithLayout == null) {
earliestUsefulChild.layout(
_gridDelegate.getLastChildLayoutType(indexOf(earliestUsefulChild)) !=
LastChildLayoutType.none
? constraints.asBoxConstraints()
: childConstraints,
parentUsesSize: true);
leadingChildWithLayout = earliestUsefulChild;
trailingChildWithLayout = earliestUsefulChild;
}
// Here, earliestUsefulChild is still the first child, it's got a
// scrollOffset that is at or before our actual scrollOffset, and it has
// been laid out, and is in fact our leadingChildWithLayout. It's possible
// that some children beyond that one have also been laid out.
bool inLayoutRange = true;
RenderBox child = earliestUsefulChild;
int index = indexOf(child);
crossAxisChildrenData.insert(
child: child,
childTrailingLayoutOffset: childTrailingLayoutOffset,
paintExtentOf: paintExtentOf,
);
bool advance() {
// returns true if we advanced, false if we have no more children
// This function is used in two different places below, to avoid code duplication.
assert(child != null);
if (child == trailingChildWithLayout) {
inLayoutRange = false;
}
child = childAfter(child);
if (child == null) {
inLayoutRange = false;
}
index += 1;
final LastChildLayoutType lastChildLayoutType =
_gridDelegate.getLastChildLayoutType(index);
final BoxConstraints currentConstraints =
lastChildLayoutType != LastChildLayoutType.none
? constraints.asBoxConstraints()
: childConstraints;
if (!inLayoutRange) {
if (child == null || indexOf(child) != index) {
// We are missing a child. Insert it (and lay it out) if possible.
child = insertAndLayoutChild(
currentConstraints,
after: trailingChildWithLayout,
parentUsesSize: true,
);
if (child == null) {
// We have run out of children.
return false;
}
} else {
// Lay out the child.
child.layout(currentConstraints, parentUsesSize: true);
}
trailingChildWithLayout = child;
}
assert(child != null);
//zmt
final SliverWaterfallFlowParentData childParentData =
child.parentData as SliverWaterfallFlowParentData;
//zmt
crossAxisChildrenData.insert(
child: child,
childTrailingLayoutOffset: childTrailingLayoutOffset,
paintExtentOf: paintExtentOf,
);
assert(childParentData.index == index);
return true;
}
final List<int> leadingGarbages = <int>[];
// Find the first child that ends after the scroll offset.
while (childTrailingLayoutOffset(child) < scrollOffset) {
leadingGarbage += 1;
leadingGarbages.add(index);
if (!advance()) {
assert(leadingGarbage == childCount);
assert(child == null);
// We ran out of children before reaching the scroll offset.
// We must inform our parent that this sliver cannot fulfill
// its contract and that we need a max scroll offset correction.
// we want to make sure we keep the trailingChildren around so we know the end scroll offset
// if _previousCrossAxisChildrenData is null, we should re-calculate it from index 0.
if (_previousCrossAxisChildrenData != null) {
final int minTrailingIndex =
_previousCrossAxisChildrenData.minTrailingIndex;
for (final int index in leadingGarbages) {
if (index >= minTrailingIndex) {
leadingGarbage -= 1;
}
}
}
collectGarbage(leadingGarbage, 0);
final double extent =
crossAxisChildrenData.maxChildTrailingLayoutOffset;
geometry = SliverGeometry(
scrollExtent: extent,
paintExtent: 0.0,
maxPaintExtent: extent,
);
//_previousCrossAxisChildrenData = null;
return;
}
}
if (leadingGarbage > 0) {
// Make sure the leadings are after the scroll offset
while (
crossAxisChildrenData.minChildTrailingLayoutOffset < scrollOffset) {
if (!advance()) {
final int minTrailingIndex = crossAxisChildrenData.minTrailingIndex;
// The indexes are continuous, make sure they are less than minTrailingIndex.
for (final int index in leadingGarbages) {
if (index >= minTrailingIndex) {
leadingGarbage--;
}
}
leadingGarbage = max(0, leadingGarbage);
break;
}
}
crossAxisChildrenData.setLeading();
}
if (child != null) {
final int crossAxisCount = _gridDelegate.getCrossAxisCount(constraints);
while (
// Now find the first child that ends after our end.
crossAxisChildrenData.minChildTrailingLayoutOffset <
targetEndScrollOffset
// Make sure leading children are all laid out.
||
crossAxisChildrenData.leadingChildren.length < crossAxisCount ||
crossAxisChildrenData.leadingChildren.length > childCount ||
(child.parentData as SliverWaterfallFlowParentData).index <
crossAxisCount - 1) {
if (!advance()) {
reachedEnd = true;
break;
}
}
}
// Finally count up all the remaining children and label them as garbage.
if (child != null) {
child = childAfter(child);
while (child != null) {
trailingGarbage += 1;
child = childAfter(child);
}
}
// At this point everything should be good to go, we just have to clean up
// the garbage and report the geometry.
collectGarbage(leadingGarbage, trailingGarbage);
//zmt
callCollectGarbage(
collectGarbage: _gridDelegate?.collectGarbage,
leadingGarbage: leadingGarbage,
trailingGarbage: trailingGarbage,
);
assert(debugAssertChildListIsNonEmptyAndContiguous());
double estimatedMaxScrollOffset;
//zmt
double endScrollOffset =
_gridDelegate.getLastChildLayoutType(indexOf(lastChild)) ==
LastChildLayoutType.none
? crossAxisChildrenData.maxChildTrailingLayoutOffset
: childTrailingLayoutOffset(lastChild);
if (reachedEnd) {
estimatedMaxScrollOffset = endScrollOffset;
} else {
estimatedMaxScrollOffset = childManager.estimateMaxScrollOffset(
constraints,
firstIndex: indexOf(firstChild),
lastIndex: indexOf(lastChild),
leadingScrollOffset: childScrollOffset(firstChild),
trailingScrollOffset: endScrollOffset,
);
//zmt
assert(estimatedMaxScrollOffset >=
endScrollOffset - childScrollOffset(firstChild));
}
///zmt
final double result =
handleCloseToTrailingEnd(closeToTrailing, endScrollOffset);
if (result != endScrollOffset) {
endScrollOffset = result;
estimatedMaxScrollOffset = result;
}
final double paintExtent = calculatePaintOffset(
constraints,
from: childScrollOffset(firstChild),
to: endScrollOffset,
);
final double cacheExtent = calculateCacheOffset(
constraints,
from: childScrollOffset(firstChild),
to: endScrollOffset,
);
final double targetEndScrollOffsetForPaint =
constraints.scrollOffset + constraints.remainingPaintExtent;
//zmt
callViewportBuilder(viewportBuilder: _gridDelegate?.viewportBuilder);
geometry = SliverGeometry(
scrollExtent: estimatedMaxScrollOffset,
paintExtent: paintExtent,
cacheExtent: cacheExtent,
maxPaintExtent: estimatedMaxScrollOffset,
// Conservative to avoid flickering away the clip during scroll.
hasVisualOverflow: endScrollOffset > targetEndScrollOffsetForPaint ||
constraints.scrollOffset > 0.0,
);
// We may have started the layout while scrolled to the end, which would not
// expose a new child.
if (estimatedMaxScrollOffset == endScrollOffset) {
childManager.setDidUnderflow(true);
}
childManager.didFinishLayout();
// Save, for next layout.
_previousCrossAxisChildrenData = crossAxisChildrenData;
}
/// Masonry layout maybe have changed. We need to recalculate from the zero index so that
/// layouts will not change suddenly when scroll.
void _clearIfNeed() {
if (_previousCrossAxisChildrenData != null) {
if (_previousCrossAxisChildrenData.crossAxisCount !=
gridDelegate.getCrossAxisCount(constraints) ||
_previousCrossAxisChildrenData.gridDelegate.mainAxisSpacing !=
gridDelegate.mainAxisSpacing ||
_previousCrossAxisChildrenData.constraints.crossAxisExtent !=
constraints.crossAxisExtent) {
_previousCrossAxisChildrenData = null;
collectGarbage(childCount, 0);
}
}
}
/// Return the trailing position of the child.
double childTrailingLayoutOffset(RenderBox child) {
return childScrollOffset(child) + paintExtentOf(child);
}
}
/// Data structure used to calculate masonry layout by [RenderSliverMasonryGrid]
class _CrossAxisChildrenData {
_CrossAxisChildrenData({
@required this.gridDelegate,
@required this.constraints,
List<SliverWaterfallFlowParentData> leadingChildren,
}) : leadingChildren =
leadingChildren?.toList() ?? <SliverWaterfallFlowParentData>[],
trailingChildren =
leadingChildren?.toList() ?? <SliverWaterfallFlowParentData>[];
/// The parent data of leading children.
final List<SliverWaterfallFlowParentData> leadingChildren;
/// The parent data of trailing children.
final List<SliverWaterfallFlowParentData> trailingChildren;
/// A delegate that controls the masonry layout of the children within the [MasonryGridView].
final SliverWaterfallFlowDelegate gridDelegate;
/// Immutable layout constraints for [RenderSliverMasonryGrid] layout.
final SliverConstraints constraints;
/// The number of children in the cross axis.
int get crossAxisCount => gridDelegate.getCrossAxisCount(constraints);
/// Fill the leading at the beginning then the children are
/// laid out after the shortest one.
void insert({
@required RenderBox child,
@required double Function(RenderBox child) childTrailingLayoutOffset,
@required double Function(RenderBox child) paintExtentOf,
}) {
final SliverWaterfallFlowParentData data =
child.parentData as SliverWaterfallFlowParentData;
final LastChildLayoutType lastChildLayoutType =
gridDelegate.getLastChildLayoutType(data.index);
switch (lastChildLayoutType) {
case LastChildLayoutType.fullCrossAxisExtent:
case LastChildLayoutType.foot:
data.crossAxisOffset = 0.0;
data.crossAxisIndex = 0;
final double size = paintExtentOf(child);
if (lastChildLayoutType == LastChildLayoutType.fullCrossAxisExtent ||
maxChildTrailingLayoutOffset + size >
constraints.remainingPaintExtent ||
gridDelegate.closeToTrailing) {
data.layoutOffset = maxChildTrailingLayoutOffset;
} else {
data.layoutOffset = constraints.remainingPaintExtent - size;
}
data.trailingLayoutOffset = childTrailingLayoutOffset(child);
return;
case LastChildLayoutType.none:
break;
}
if (!leadingChildren.contains(data)) {
if (leadingChildren.length != crossAxisCount) {
data.crossAxisIndex ??= leadingChildren.length;
data.crossAxisOffset =
gridDelegate.getCrossAxisOffset(constraints, data.crossAxisIndex);
data.layoutOffset = 0.0;
data.indexes.clear();
trailingChildren.add(data);
leadingChildren.add(data);
}
// The child after the leading should be put into the trailing.
else {
if (data.crossAxisIndex != null) {
final SliverWaterfallFlowParentData item =
trailingChildren.firstWhere(
(SliverWaterfallFlowParentData x) =>
x.index > data.index && x.crossAxisIndex == data.crossAxisIndex,
orElse: () => null,
);
// It is out of the viewport.
// It happens when one child has large size in the main axis,
if (item != null) {
data.trailingLayoutOffset = childTrailingLayoutOffset(child);
return;
}
}
// Find the shortest one and laid out after it.
final SliverWaterfallFlowParentData min = trailingChildren.reduce(
(SliverWaterfallFlowParentData current,
SliverWaterfallFlowParentData next) =>
current.trailingLayoutOffset < next.trailingLayoutOffset ||
(current.trailingLayoutOffset ==
next.trailingLayoutOffset &&
current.crossAxisIndex < next.crossAxisIndex)
? current
: next,
);
data.layoutOffset =
min.trailingLayoutOffset + gridDelegate.mainAxisSpacing;
data.crossAxisIndex = min.crossAxisIndex;
data.crossAxisOffset =
gridDelegate.getCrossAxisOffset(constraints, data.crossAxisIndex);
for (final SliverWaterfallFlowParentData parentData
in trailingChildren) {
parentData.indexes.remove(min.index);
}
min.indexes.add(min.index);
data.indexes = min.indexes;
trailingChildren.remove(min);
trailingChildren.add(data);
}
}
data.crossAxisOffset =
gridDelegate.getCrossAxisOffset(constraints, data.crossAxisIndex);
data.trailingLayoutOffset = childTrailingLayoutOffset(child);
}
/// When maxLeadingLayoutOffset is less than scrollOffset,
/// we have to insert child before the scrollOffset base on the leadings.
void insertLeading({
@required RenderBox child,
@required double Function(RenderBox child) paintExtentOf,
}) {
final SliverWaterfallFlowParentData data =
child.parentData as SliverWaterfallFlowParentData;
if (!leadingChildren.contains(data)) {
final SliverWaterfallFlowParentData leading = leadingChildren.firstWhere(
(SliverWaterfallFlowParentData x) => x.indexes.contains(data.index),
orElse: () => null,
);
// This child is after the leadings.
if (leading == null || data.index > leading.index) {
return;
}
// Laid out the child before the leading.
data.trailingLayoutOffset =
leading.layoutOffset - gridDelegate.mainAxisSpacing;
data.layoutOffset = data.trailingLayoutOffset - paintExtentOf(child);
data.crossAxisIndex = leading.crossAxisIndex;
data.crossAxisOffset =
gridDelegate.getCrossAxisOffset(constraints, data.crossAxisIndex);
data.indexes = leading.indexes;
leadingChildren.remove(leading);
trailingChildren.remove(leading);
leadingChildren.add(data);
trailingChildren.add(data);
}
}
double get maxLeadingLayoutOffset {
if (leadingChildren.isEmpty) {
return 0.0;
}
return leadingChildren
.reduce((SliverWaterfallFlowParentData current,
SliverWaterfallFlowParentData next) =>
current.layoutOffset >= next.layoutOffset ? current : next)
.layoutOffset;
}
double get minChildTrailingLayoutOffset {
if (trailingChildren.isEmpty) {
return 0.0;
}
return trailingChildren
.reduce((SliverWaterfallFlowParentData current,
SliverWaterfallFlowParentData next) =>
current.trailingLayoutOffset <= next.trailingLayoutOffset
? current
: next)
.trailingLayoutOffset;
}
double get maxChildTrailingLayoutOffset {
if (trailingChildren.isEmpty) {
return 0.0;
}
return trailingChildren
.reduce((SliverWaterfallFlowParentData current,
SliverWaterfallFlowParentData next) =>
current.trailingLayoutOffset >= next.trailingLayoutOffset
? current
: next)
.trailingLayoutOffset;
}
int get minLeadingIndex {
if (leadingChildren.isEmpty) {
return -1;
}
return leadingChildren
.reduce((SliverWaterfallFlowParentData current,
SliverWaterfallFlowParentData next) =>
current.index < next.index ? current : next)
.index;
}
int get maxLeadingIndex {
if (leadingChildren.isEmpty) {
return -1;
}
return leadingChildren
.reduce((SliverWaterfallFlowParentData current,
SliverWaterfallFlowParentData next) =>
current.index > next.index ? current : next)
.index;
}
int get minTrailingIndex {
if (trailingChildren.isEmpty) {
return -1;
}
return trailingChildren
.reduce((SliverWaterfallFlowParentData current,
SliverWaterfallFlowParentData next) =>
current.index < next.index ? current : next)
.index;
}
void clear() {
leadingChildren.clear();
trailingChildren.clear();
}
/// Called after all of the leading are after the scroll offset
/// That is the final leading.
void setLeading() {
leadingChildren.clear();
leadingChildren.addAll(trailingChildren);
}
}
import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart' hide ViewportBuilder;
import 'package:waterfall_flow/src/extended/typedef.dart';
import 'package:waterfall_flow/src/rendering/sliver_waterfall_flow.dart';
import 'sliver.dart';
///
/// create by zmtzawqlp on 2019/11/9
///
/// A scrollable, 2D array of widgets whose size is variable in the main axis.
///
/// The main axis direction of a grid is the direction in which it scrolls (the
/// [scrollDirection]).
///
/// The most commonly used waterfall layouts are [WaterfallFlow.count], which creates a
/// layout with a fixed number of tiles in the cross axis.
///
/// To create a grid with a large (or infinite) number of children, use the
/// [WaterfallFlow.builder] constructor with a [SliverWaterfallFlowDelegate]
/// for the [gridDelegate].
///
/// To use a custom [SliverWaterfallFlowDelegate], use [WaterfallFlow.custom].
///
/// To create a linear array of children, use a [ListView].
///
/// To control the initial scroll offset of the scroll view, provide a
/// [controller] with its [ScrollController.initialScrollOffset] property set.
///
/// ## Transitioning to [CustomScrollView]
///
/// A [WaterfallFlow] is basically a [CustomScrollView] with a single [SliverWaterfallFlow] in
/// its [CustomScrollView.slivers] property.
///
/// If [WaterfallFlow] is no longer sufficient, for example because the scroll view
/// is to have both a grid and a list, or because the grid is to be combined
/// with a [SliverAppBar], etc, it is straight-forward to port code from using
/// [WaterfallFlow] to using [CustomScrollView] directly.
///
/// The [key], [scrollDirection], [reverse], [controller], [primary], [physics],
/// and [shrinkWrap] properties on [WaterfallFlow] map directly to the identically
/// named properties on [CustomScrollView].
///
/// The [CustomScrollView.slivers] property should be a list containing just a
/// [SliverWaterfallFlow].
///
/// The [childrenDelegate] property on [WaterfallFlow] corresponds to the
/// [SliverWaterfallFlow.gridDelegate] property, and the [gridDelegate] property on the
/// [WaterfallFlow] corresponds to the [SliverWaterfallFlow.gridDelegate] property.
///
/// The [WaterfallFlow] and [WaterfallFlow.count] constructors' `children` arguments
/// correspond to the [childrenDelegate] being a [SliverChildListDelegate]
/// with that same argument.
/// The [WaterfallFlow.builder] constructor's `itemBuilder` and `childCount` arguments
/// correspond to the [childrenDelegate] being a [SliverChildBuilderDelegate]
/// with the matching arguments.
///
/// The [WaterfallFlow.count] constructors create custom grid delegates,
/// and have equivalently named constructor on [SliverWaterfallFlow] to
/// ease the transition: [SliverWaterfallFlow.count] respectively.
///
/// The [padding] property corresponds to having a [SliverPadding] in the
/// [CustomScrollView.slivers] property instead of the grid itself, and having
/// the [SliverWaterfallFlow] instead be a child of the [SliverPadding].
///
/// By default, [ListView] will automatically pad the list's scrollable
/// extremities to avoid partial obstructions indicated by [MediaQuery]'s
/// padding. To avoid this behavior, override with a zero [padding] property.
///
/// Once code has been ported to use [CustomScrollView], other slivers, such as
/// [SliverList] or [SliverAppBar], can be put in the [CustomScrollView.slivers]
/// list.
///
/// {@tool snippet}
/// This example demonstrates how to create a [WaterfallFlow] with two columns. The
/// children are spaced apart using the [crossAxisSpacing] and [mainAxisSpacing]
/// properties.
///
/// ![The WaterfallFlow displays six children with different background colors and heights arranged in two columns](https://flutter.github.io/assets-for-api-docs/assets/widgets/grid_view.png)
///
/// ```dart
/// WaterfallFlow.count(
/// primary: false,
/// padding: const EdgeInsets.all(20),
/// crossAxisSpacing: 10,
/// mainAxisSpacing: 10,
/// crossAxisCount: 2,
/// children: <Widget>[
/// Container(
/// padding: const EdgeInsets.all(8),
/// child: const Text("0.He'd have you all unravel at the"),
/// color: Colors.teal[100],
/// height: 50.0,
/// ),
/// Container(
/// padding: const EdgeInsets.all(8),
/// child: const Text('1.Heed not the rabble'),
/// color: Colors.teal[200],
/// height: 70.0,
/// ),
/// Container(
/// padding: const EdgeInsets.all(8),
/// child: const Text('2.Sound of screams but the'),
/// color: Colors.teal[300],
/// height: 90.0,
/// ),
/// Container(
/// padding: const EdgeInsets.all(8),
/// child: const Text('3.Who scream'),
/// color: Colors.teal[400],
/// height: 60.0,
/// ),
/// Container(
/// padding: const EdgeInsets.all(8),
/// child: const Text('4.Revolution is coming...'),
/// color: Colors.teal[500],
/// height: 80.0,
/// ),
/// Container(
/// padding: const EdgeInsets.all(8),
/// child: const Text('5.Revolution, they...'),
/// color: Colors.teal[600],
/// height: 100.0,
/// ),
/// ],
/// )
/// ```
/// {@end-tool}
///
/// {@tool snippet}
/// This example shows how to create the same grid as the previous example
/// using a [CustomScrollView] and a [SliverWaterfallFlow].
///
/// ![The CustomScrollView contains a SliverWaterfallFlow that displays six children with different background colors and heights arranged in two columns](https://flutter.github.io/assets-for-api-docs/assets/widgets/grid_view_custom_scroll.png)
///
/// ```dart
/// CustomScrollView(
/// primary: false,
/// slivers: <Widget>[
/// SliverPadding(
/// padding: const EdgeInsets.all(20),
/// sliver: SliverWaterfallFlow.count(
/// crossAxisSpacing: 10,
/// mainAxisSpacing: 10,
/// crossAxisCount: 2,
/// children: <Widget>[
/// Container(
/// padding: const EdgeInsets.all(8),
/// child: const Text("0.He'd have you all unravel at the"),
/// color: Colors.green[100],
/// height: 50.0,
/// ),
/// Container(
/// padding: const EdgeInsets.all(8),
/// child: const Text('1.Heed not the rabble'),
/// color: Colors.green[200],
/// height: 70.0,
/// ),
/// Container(
/// padding: const EdgeInsets.all(8),
/// child: const Text('2.Sound of screams but the'),
/// color: Colors.green[300],
/// height: 90.0,
/// ),
/// Container(
/// padding: const EdgeInsets.all(8),
/// child: const Text('3.Who scream'),
/// color: Colors.green[400],
/// height: 60.0,
/// ),
/// Container(
/// padding: const EdgeInsets.all(8),
/// child: const Text('4.Revolution is coming...'),
/// color: Colors.green[500],
/// height: 80.0,
/// ),
/// Container(
/// padding: const EdgeInsets.all(8),
/// child: const Text('5.Revolution, they...'),
/// color: Colors.green[600],
/// height: 100.0,
/// ),
/// ],
/// ),
/// ),
/// ],
/// )
/// ```
/// {@end-tool}
///
/// See also:
///
/// * [SingleChildScrollView], which is a scrollable widget that has a single
/// child.
/// * [ListView], which is scrollable, linear list of widgets.
/// * [GridView], which is scrollable, 2D array of widgets.
/// * [PageView], which is a scrolling list of child widgets that are each the
/// size of the viewport.
/// * [CustomScrollView], which is a scrollable widget that creates custom
/// scroll effects using slivers.
/// * [SliverGridDelegateWithFixedCrossAxisCount], which creates a layout with
/// a fixed number of tiles in the cross axis.
/// * [SliverGridDelegateWithMaxCrossAxisExtent], which creates a layout with
/// tiles that have a maximum cross-axis extent.
/// * [ScrollNotification] and [NotificationListener], which can be used to watch
/// the scroll position without using a [ScrollController].
class WaterfallFlow extends BoxScrollView {
/// Creates a scrollable, 2D array of widgets whose size is variable in the main axis
/// with a custom [SliverWaterfallFlowDelegate].
///
/// The [gridDelegate] argument must not be null.
///
/// The `addAutomaticKeepAlives` argument corresponds to the
/// [SliverChildListDelegate.addAutomaticKeepAlives] property. The
/// `addRepaintBoundaries` argument corresponds to the
/// [SliverChildListDelegate.addRepaintBoundaries] property. Both must not be
/// null.
WaterfallFlow({
Key key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
EdgeInsetsGeometry padding,
@required this.gridDelegate,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
bool addSemanticIndexes = true,
double cacheExtent,
List<Widget> children = const <Widget>[],
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
ScrollViewKeyboardDismissBehavior keyboardDismissBehavior =
ScrollViewKeyboardDismissBehavior.manual,
}) : assert(gridDelegate != null),
childrenDelegate = SliverChildListDelegate(
children,
addAutomaticKeepAlives: addAutomaticKeepAlives,
addRepaintBoundaries: addRepaintBoundaries,
addSemanticIndexes: addSemanticIndexes,
),
super(
key: key,
scrollDirection: scrollDirection,
reverse: reverse,
controller: controller,
primary: primary,
physics: physics,
shrinkWrap: shrinkWrap,
padding: padding,
cacheExtent: cacheExtent,
semanticChildCount: semanticChildCount ?? children.length,
dragStartBehavior: dragStartBehavior,
keyboardDismissBehavior: keyboardDismissBehavior,
);
/// Creates a scrollable, 2D array of widgets whose size is variable in the main axis
/// that are created on demand.
///
/// This constructor is appropriate for grid views with a large (or infinite)
/// number of children because the builder is called only for those children
/// that are actually visible.
///
/// Providing a non-null `itemCount` improves the ability of the [WaterfallFlow] to
/// estimate the maximum scroll extent.
///
/// `itemBuilder` will be called only with indices greater than or equal to
/// zero and less than `itemCount`.
///
/// The [gridDelegate] argument must not be null.
///
/// The `addAutomaticKeepAlives` argument corresponds to the
/// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
/// `addRepaintBoundaries` argument corresponds to the
/// [SliverChildBuilderDelegate.addRepaintBoundaries] property. Both must not
/// be null.
WaterfallFlow.builder({
Key key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
EdgeInsetsGeometry padding,
@required this.gridDelegate,
@required IndexedWidgetBuilder itemBuilder,
int itemCount,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
bool addSemanticIndexes = true,
double cacheExtent,
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
ScrollViewKeyboardDismissBehavior keyboardDismissBehavior =
ScrollViewKeyboardDismissBehavior.manual,
}) : assert(gridDelegate != null),
childrenDelegate = SliverChildBuilderDelegate(
itemBuilder,
childCount: itemCount,
addAutomaticKeepAlives: addAutomaticKeepAlives,
addRepaintBoundaries: addRepaintBoundaries,
addSemanticIndexes: addSemanticIndexes,
),
super(
key: key,
scrollDirection: scrollDirection,
reverse: reverse,
controller: controller,
primary: primary,
physics: physics,
shrinkWrap: shrinkWrap,
padding: padding,
cacheExtent: cacheExtent,
semanticChildCount: semanticChildCount ?? itemCount,
dragStartBehavior: dragStartBehavior,
keyboardDismissBehavior: keyboardDismissBehavior,
);
/// Creates a scrollable, 2D array of widgets whose size is variable in the main axis
/// with both a custom [SliverGridDelegate] and a custom [SliverChildDelegate].
///
/// To use an [IndexedWidgetBuilder] callback to build children, either use
/// a [SliverChildBuilderDelegate] or use the [WaterfallFlow.builder] constructor.
///
/// The [gridDelegate] and [childrenDelegate] arguments must not be null.
const WaterfallFlow.custom({
Key key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
EdgeInsetsGeometry padding,
@required this.gridDelegate,
@required this.childrenDelegate,
double cacheExtent,
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
ScrollViewKeyboardDismissBehavior keyboardDismissBehavior =
ScrollViewKeyboardDismissBehavior.manual,
}) : assert(gridDelegate != null),
assert(childrenDelegate != null),
super(
key: key,
scrollDirection: scrollDirection,
reverse: reverse,
controller: controller,
primary: primary,
physics: physics,
shrinkWrap: shrinkWrap,
padding: padding,
cacheExtent: cacheExtent,
semanticChildCount: semanticChildCount,
dragStartBehavior: dragStartBehavior,
keyboardDismissBehavior: keyboardDismissBehavior,
);
/// Creates a scrollable, 2D array of widgets whose size is variable in the main axis
/// with a fixed number of tiles in the cross axis.
///
/// Uses a [SliverWaterfallFlowDelegate] as the [gridDelegate].
///
/// The `addAutomaticKeepAlives` argument corresponds to the
/// [SliverChildListDelegate.addAutomaticKeepAlives] property. The
/// `addRepaintBoundaries` argument corresponds to the
/// [SliverChildListDelegate.addRepaintBoundaries] property. Both must not be
/// null.
///
/// See also:
///
/// * [new SliverWaterfallFlow.count], the equivalent constructor for [SliverWaterfallFlow].
WaterfallFlow.count({
Key key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
EdgeInsetsGeometry padding,
@required int crossAxisCount,
double mainAxisSpacing = 0.0,
double crossAxisSpacing = 0.0,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
bool addSemanticIndexes = true,
double cacheExtent,
List<Widget> children = const <Widget>[],
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
ScrollViewKeyboardDismissBehavior keyboardDismissBehavior =
ScrollViewKeyboardDismissBehavior.manual,
LastChildLayoutTypeBuilder lastChildLayoutTypeBuilder,
CollectGarbage collectGarbage,
ViewportBuilder viewportBuilder,
bool closeToTrailing = false,
}) : gridDelegate = SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
lastChildLayoutTypeBuilder: lastChildLayoutTypeBuilder,
collectGarbage: collectGarbage,
viewportBuilder: viewportBuilder,
closeToTrailing: closeToTrailing,
),
childrenDelegate = SliverChildListDelegate(
children,
addAutomaticKeepAlives: addAutomaticKeepAlives,
addRepaintBoundaries: addRepaintBoundaries,
addSemanticIndexes: addSemanticIndexes,
),
super(
key: key,
scrollDirection: scrollDirection,
reverse: reverse,
controller: controller,
primary: primary,
physics: physics,
shrinkWrap: shrinkWrap,
padding: padding,
cacheExtent: cacheExtent,
semanticChildCount: semanticChildCount ?? children.length,
dragStartBehavior: dragStartBehavior,
keyboardDismissBehavior: keyboardDismissBehavior,
);
/// Creates a scrollable, 2D array of widgets in masonry layout with tiles
/// that each have a maximum cross-axis extent.
///
/// Uses a [SliverMasonryGridDelegateWithMaxCrossAxisExtent] as the [gridDelegate].
///
/// The `addAutomaticKeepAlives` argument corresponds to the
/// [SliverChildListDelegate.addAutomaticKeepAlives] property. The
/// `addRepaintBoundaries` argument corresponds to the
/// [SliverChildListDelegate.addRepaintBoundaries] property. Both must not be
/// null.
///
/// See also:
///
/// * [SliverGrid.extent], the equivalent constructor for [SliverGrid].
WaterfallFlow.extent({
Key key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
EdgeInsetsGeometry padding,
@required double maxCrossAxisExtent,
double mainAxisSpacing = 0.0,
double crossAxisSpacing = 0.0,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
bool addSemanticIndexes = true,
List<Widget> children = const <Widget>[],
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
ScrollViewKeyboardDismissBehavior keyboardDismissBehavior =
ScrollViewKeyboardDismissBehavior.manual,
LastChildLayoutTypeBuilder lastChildLayoutTypeBuilder,
CollectGarbage collectGarbage,
ViewportBuilder viewportBuilder,
bool closeToTrailing = false,
}) : gridDelegate = SliverWaterfallFlowDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: maxCrossAxisExtent,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
lastChildLayoutTypeBuilder: lastChildLayoutTypeBuilder,
collectGarbage: collectGarbage,
viewportBuilder: viewportBuilder,
closeToTrailing: closeToTrailing,
),
childrenDelegate = SliverChildListDelegate(
children,
addAutomaticKeepAlives: addAutomaticKeepAlives,
addRepaintBoundaries: addRepaintBoundaries,
addSemanticIndexes: addSemanticIndexes,
),
super(
key: key,
scrollDirection: scrollDirection,
reverse: reverse,
controller: controller,
primary: primary,
physics: physics,
shrinkWrap: shrinkWrap,
padding: padding,
semanticChildCount: semanticChildCount ?? children.length,
dragStartBehavior: dragStartBehavior,
keyboardDismissBehavior: keyboardDismissBehavior,
);
/// A delegate that controls the masonry layout of the children within the [WaterfallFlow].
///
/// The [WaterfallFlow], [WaterfallFlow.builder], and [WaterfallFlow.custom] constructors let you specify this
/// delegate explicitly. The other constructors create a [gridDelegate]
/// implicitly.
final SliverWaterfallFlowDelegate gridDelegate;
/// A delegate that provides the children for the [WaterfallFlow].
///
/// The [WaterfallFlow.custom] constructor lets you specify this delegate
/// explicitly. The other constructors create a [childrenDelegate] that wraps
/// the given child list.
final SliverChildDelegate childrenDelegate;
@override
Widget buildChildLayout(BuildContext context) {
return SliverWaterfallFlow(
delegate: childrenDelegate,
gridDelegate: gridDelegate,
);
}
}
///
/// create by zmtzawqlp on 2019/11/9
///
import 'package:flutter/widgets.dart' hide ViewportBuilder;
import 'package:waterfall_flow/src/extended/typedef.dart';
import 'package:waterfall_flow/src/rendering/sliver_waterfall_flow.dart';
/// A sliver that places multiple box children in a two dimensional arrangement
/// and masonry layout.
///
/// [SliverWaterfallFlow] places its children in arbitrary positions determined by
/// [gridDelegate]. Each child is forced to have the size specified by the
/// [gridDelegate].
///
/// The main axis direction of a grid is the direction in which it scrolls; the
/// cross axis direction is the orthogonal direction.
///
///
/// This example, which would be inserted into a [CustomScrollView.slivers]
/// list, shows twenty boxes in a pretty teal grid with masonry layout:
///
/// ```dart
/// SliverWaterfallFlow(
/// gridDelegate: SliverWaterfallFlowDelegate(
/// crossAxisCount: 2,
/// mainAxisSpacing: 10.0,
/// crossAxisSpacing: 10.0,
/// ),
/// delegate: SliverChildBuilderDelegate(
/// (BuildContext context, int index) {
/// return Container(
/// alignment: Alignment.center,
/// color: Colors.teal[100 * (index % 9)],
/// child: Text('grid item $index'),
/// height: 50.0 + 100.0 * (index % 9)
/// );
/// },
/// childCount: 20,
/// ),
/// )
/// ```
/// {@end-tool}
///
/// {@macro flutter.widgets.sliverChildDelegate.lifecycle}
///
/// See also:
///
/// * [SliverList], which places its children in a linear array.
/// * [SliverFixedExtentList], which places its children in a linear
/// array with a fixed extent in the main axis.
/// * [SliverPrototypeExtentList], which is similar to [SliverFixedExtentList]
/// except that it uses a prototype list item instead of a pixel value to define
/// the main axis extent of each item.
/// * [SliverGrid], which places its children in arbitrary positions.
class SliverWaterfallFlow extends SliverMultiBoxAdaptorWidget {
/// Creates a sliver that places multiple box children in a two dimensional
/// arrangement and masonry layout.
const SliverWaterfallFlow({
Key key,
@required SliverChildDelegate delegate,
@required this.gridDelegate,
}) : super(key: key, delegate: delegate);
/// Creates a sliver that places multiple box children in a two dimensional
/// arrangement and masonry layout with a fixed number of tiles in the cross axis.
///
/// Uses a [SliverWaterfallFlowDelegate] as the [gridDelegate],
/// and a [SliverChildListDelegate] as the [delegate].
///
/// See also:
///
/// * [new WaterfallFlow.count], the equivalent constructor for [WaterfallFlow] widgets.
SliverWaterfallFlow.count({
Key key,
@required int crossAxisCount,
double mainAxisSpacing = 0.0,
double crossAxisSpacing = 0.0,
List<Widget> children = const <Widget>[],
LastChildLayoutTypeBuilder lastChildLayoutTypeBuilder,
CollectGarbage collectGarbage,
ViewportBuilder viewportBuilder,
bool closeToTrailing = false,
}) : gridDelegate = SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
lastChildLayoutTypeBuilder: lastChildLayoutTypeBuilder,
collectGarbage: collectGarbage,
viewportBuilder: viewportBuilder,
closeToTrailing: closeToTrailing,
),
super(key: key, delegate: SliverChildListDelegate(children));
/// Creates a sliver that places multiple box children in masonry layout
/// with tiles that each have a maximum cross-axis extent.
///
/// Uses a [SliverMasonryGridDelegateWithMaxCrossAxisExtent] as the [gridDelegate],
/// and a [SliverChildListDelegate] as the [delegate].
///
/// See also:
///
/// * [new MasonryGridView.extent], the equivalent constructor for [MasonryGridView] widgets.
SliverWaterfallFlow.extent({
Key key,
@required double maxCrossAxisExtent,
double mainAxisSpacing = 0.0,
double crossAxisSpacing = 0.0,
List<Widget> children = const <Widget>[],
LastChildLayoutTypeBuilder lastChildLayoutTypeBuilder,
CollectGarbage collectGarbage,
ViewportBuilder viewportBuilder,
bool closeToTrailing = false,
}) : gridDelegate = SliverWaterfallFlowDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: maxCrossAxisExtent,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
lastChildLayoutTypeBuilder: lastChildLayoutTypeBuilder,
collectGarbage: collectGarbage,
viewportBuilder: viewportBuilder,
closeToTrailing: closeToTrailing,
),
super(key: key, delegate: SliverChildListDelegate(children));
/// The delegate that controls the size and position of the children.
final SliverWaterfallFlowDelegate gridDelegate;
@override
RenderSliverWaterfallFlow createRenderObject(BuildContext context) {
final SliverMultiBoxAdaptorElement element = context as SliverMultiBoxAdaptorElement;
return RenderSliverWaterfallFlow(childManager: element, gridDelegate: gridDelegate);
}
@override
void updateRenderObject(BuildContext context, RenderSliverWaterfallFlow renderObject) {
renderObject.gridDelegate = gridDelegate;
}
}
library waterfall_flow;
export 'src/extended/extended_list_library.dart';
export 'src/rendering/sliver_waterfall_flow.dart';
export 'src/widgets/scroll_view.dart';
export 'src/widgets/sliver.dart';
export 'xxwaterfall_flow.dart';
\ No newline at end of file
import 'package:flutter/widgets.dart';
import 'package:waterfall_flow/src/widgets/scroll_view.dart';
import 'src/rendering/sliver_waterfall_flow.dart';
class XXSliverWaterFallFlow extends StatefulWidget {
final Widget Function(BuildContext, int) itemBuilder;
final void Function(int firstIndex, int lastIndex) onScrollInit;
final void Function(int firstIndex, int lastIndex) onScrollEnd;
const XXSliverWaterFallFlow({ Key key,@required this.itemBuilder,this.onScrollInit,this.onScrollEnd}) : super(key: key);
@override
_XXSliverWaterFallFlowState createState() => _XXSliverWaterFallFlowState();
}
class _XXSliverWaterFallFlowState extends State<XXSliverWaterFallFlow> {
bool _isInit = true;
int _firstIndex;
int _lastIndex;
@override
Widget build(BuildContext context) {
return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) {
if (notification.runtimeType == ScrollEndNotification) {
if(widget.onScrollEnd != null){
widget.onScrollEnd(_firstIndex,_lastIndex);
}
}
return true;
},
child:WaterfallFlow.builder(
gridDelegate:SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
crossAxisCount: 5,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5.0,
viewportBuilder:(int firstIndex, int lastIndex) {
_firstIndex = firstIndex;
_lastIndex = lastIndex;
if(_isInit && widget.onScrollInit != null){
widget.onScrollInit(_firstIndex,_lastIndex);
_isInit = false;
}
}
),
itemBuilder:widget.itemBuilder,
),
);
}
}
\ No newline at end of file
name: waterfall_flow
description: A Flutter grid view that build waterfall flow layout quickly.
version: 2.0.5
homepage: https://github.com/fluttercandies/waterfall_flow
environment:
sdk: ">=2.6.0 <3.0.0"
flutter: ">=1.22.4"
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
\ No newline at end of file
// @dart = 2.8
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:waterfall_flow/waterfall_flow.dart';
void main() {
group('SliverWaterfallFlow', () {
testWidgets('the size of each child', (WidgetTester tester) async {
await tester.pumpWidget(materialAppBoilerplate(
child: customScrollViewBoilerplate(
sliverMasonryGrid:
sliverMasonryGridWithChildrenBoilerplate(crossAxisCount: 2),
),
textDirection: TextDirection.ltr,
));
expect(
tester.getSize(find.widgetWithText(
Container, "0.He'd have you all unravel at the")),
const Size(400.0, 50.0),
);
expect(
tester.getSize(find.widgetWithText(Container, '1.Heed not the rabble')),
const Size(400.0, 70.0),
);
expect(
tester.getSize(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Size(400.0, 90.0),
);
expect(
tester.getSize(find.widgetWithText(Container, '3.Who scream')),
const Size(400.0, 60.0),
);
expect(
tester.getSize(
find.widgetWithText(Container, '4.Revolution is coming...')),
const Size(400.0, 80.0),
);
expect(
tester.getSize(find.widgetWithText(Container, '5.Revolution, they...')),
const Size(400.0, 100.0),
);
});
testWidgets('the position of each child at TextDirection.ltr',
(WidgetTester tester) async {
await tester.pumpWidget(materialAppBoilerplate(
child: customScrollViewBoilerplate(
sliverMasonryGrid:
sliverMasonryGridWithChildrenBoilerplate(crossAxisCount: 2),
),
textDirection: TextDirection.ltr,
));
expect(
tester.getTopLeft(find.widgetWithText(
Container, "0.He'd have you all unravel at the")),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '1.Heed not the rabble')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(0.0, 50.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3.Who scream')),
const Offset(400.0, 70.0),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '4.Revolution is coming...')),
const Offset(400.0, 130.0),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '5.Revolution, they...')),
const Offset(0.0, 140.0),
);
});
testWidgets('the position of each child at TextDirection.rtl',
(WidgetTester tester) async {
await tester.pumpWidget(
materialAppBoilerplate(
child: customScrollViewBoilerplate(
sliverMasonryGrid:
sliverMasonryGridWithChildrenBoilerplate(crossAxisCount: 2),
),
textDirection: TextDirection.rtl),
);
expect(
tester.getTopLeft(find.widgetWithText(
Container, "0.He'd have you all unravel at the")),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '1.Heed not the rabble')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(400.0, 50.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3.Who scream')),
const Offset(0.0, 70.0),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '4.Revolution is coming...')),
const Offset(0.0, 130.0),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '5.Revolution, they...')),
const Offset(400.0, 140.0),
);
});
testWidgets('crossAxisCount change test', (WidgetTester tester) async {
int crossAxisCount = 2;
await tester.pumpWidget(
materialAppBoilerplate(
child: SliverMasonryTestPage(
crossAxisCount: crossAxisCount,
setState: (_SliverMasonryTestPageState state) {
state._crossAxisCount = crossAxisCount;
},
),
textDirection: TextDirection.ltr),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(0.0, 50.0),
);
crossAxisCount = 4;
await setStateBoilerplate(tester);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(400.0, 0.0),
);
});
testWidgets('crossAxisSpacing change test', (WidgetTester tester) async {
double crossAxisSpacing = 10;
await tester.pumpWidget(
materialAppBoilerplate(
child: SliverMasonryTestPage(
crossAxisSpacing: crossAxisSpacing,
crossAxisCount: 2,
setState: (_SliverMasonryTestPageState state) {
state._crossAxisSpacing = crossAxisSpacing;
},
),
textDirection: TextDirection.ltr),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(0.0, 50.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3.Who scream')),
const Offset(405.0, 70.0),
);
crossAxisSpacing = 20;
await setStateBoilerplate(tester);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(0.0, 50.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3.Who scream')),
const Offset(410.0, 70.0),
);
});
testWidgets('mainAxisSpacing test', (WidgetTester tester) async {
double mainAxisSpacing = 10;
await tester.pumpWidget(
materialAppBoilerplate(
child: SliverMasonryTestPage(
mainAxisSpacing: mainAxisSpacing,
crossAxisCount: 2,
setState: (_SliverMasonryTestPageState state) {
state._mainAxisSpacing = mainAxisSpacing;
},
),
textDirection: TextDirection.ltr),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(0.0, 60.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3.Who scream')),
const Offset(400.0, 80.0),
);
mainAxisSpacing = 20;
await setStateBoilerplate(tester);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(0.0, 70.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3.Who scream')),
const Offset(400.0, 90.0),
);
});
testWidgets('maxCrossAxisExtent change test', (WidgetTester tester) async {
double maxCrossAxisExtent = 400;
await tester.pumpWidget(
materialAppBoilerplate(
child: SliverMasonryTestPage(
maxCrossAxisExtent: maxCrossAxisExtent,
crossAxisCount: 2,
setState: (_SliverMasonryTestPageState state) {
state._maxCrossAxisExtent = maxCrossAxisExtent;
},
),
textDirection: TextDirection.ltr),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(0.0, 50.0),
);
maxCrossAxisExtent = 200;
await setStateBoilerplate(tester);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(400.0, 0.0),
);
});
testWidgets('itemCount change test', (WidgetTester tester) async {
final ScrollController controller = ScrollController();
int itemCount = 100;
await tester.pumpWidget(
materialAppBoilerplate(
child: SliverMasonryTestPage(
controller: controller,
crossAxisCount: 4,
items: List<int>.generate(itemCount, (int index) => index),
setState: (_SliverMasonryTestPageState state) {
state._items = List<int>.generate(itemCount, (int index) => index);
},
builder: true,
)),
);
expect(find.widgetWithText(Container, '0'), findsOneWidget);
controller.jumpTo(10000);
await tester.pumpAndSettle();
itemCount = 0;
await setStateBoilerplate(tester);
expect(find.widgetWithText(Container, '0'), findsNothing);
expect(controller.offset, 0.0);
itemCount = 100;
await setStateBoilerplate(tester);
expect(find.widgetWithText(Container, '3'), findsOneWidget);
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 100.0),
);
});
testWidgets('items removeRange test', (WidgetTester tester) async {
final ScrollController controller = ScrollController();
int start;
int end;
await tester.pumpWidget(
materialAppBoilerplate(
child: SliverMasonryTestPage(
controller: controller,
crossAxisCount: 4,
items: List<int>.generate(100, (int index) => index),
setState: (_SliverMasonryTestPageState state) {
state._items.removeRange(start, end);
},
builder: true,
)),
);
expect(find.widgetWithText(Container, '0'), findsOneWidget);
controller.jumpTo(10000);
await tester.pumpAndSettle();
start = 40;
end = 60;
await setStateBoilerplate(tester);
controller.jumpTo(0);
await tester.pumpAndSettle();
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 100.0),
);
});
// code coverage: [RenderSliverMasonryGrid]
// if (data.layoutOffset < -precisionErrorTolerance)
testWidgets('the child who out of viewport change big test',
(WidgetTester tester) async {
final ScrollController controller = ScrollController();
double size;
await tester.pumpWidget(
materialAppBoilerplate(
child: SliverMasonryTestPage(
controller: controller,
crossAxisCount: 4,
items: List<int>.generate(100, (int index) => index),
sizeBuilder: (int index) {
if (index == 1 && size != null) {
return size;
}
return null;
},
builder: true,
)),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(200.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(400.0, 300.0),
);
controller.jumpTo(10000);
await tester.pumpAndSettle();
expect(find.widgetWithText(Container, '0'), findsNothing);
// change child size from 200 to 500.
size = 500;
await setStateBoilerplate(tester);
controller.jumpTo(0);
await tester.pumpAndSettle();
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(400.0, 300.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(0.0, 400.0),
);
});
// code coverage: [RenderSliverMasonryGrid]
// if (earliestUsefulChild == null) {
testWidgets('the child who out of viewport change small sroll into 0 test',
(WidgetTester tester) async {
final ScrollController controller = ScrollController();
double size;
await tester.pumpWidget(
materialAppBoilerplate(
child: SliverMasonryTestPage(
controller: controller,
crossAxisCount: 4,
items: List<int>.generate(100, (int index) => index),
sizeBuilder: (int index) {
if (index == 0 && size != null) {
return size;
}
return null;
},
builder: true,
)),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(200.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(400.0, 300.0),
);
controller.jumpTo(10000);
await tester.pumpAndSettle();
expect(find.widgetWithText(Container, '0'), findsNothing);
// change child size from 100 to 30.
size = 30;
await setStateBoilerplate(tester);
controller.jumpTo(0);
await tester.pumpAndSettle();
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 30.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 130.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(200.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(400.0, 300.0),
);
});
// code coverage: [RenderSliverMasonryGrid]
// if (earliestUsefulChild == null) {
testWidgets(
'the child who out of viewport change small sroll into 100 test',
(WidgetTester tester) async {
final ScrollController controller = ScrollController();
double size;
await tester.pumpWidget(
materialAppBoilerplate(
child: SliverMasonryTestPage(
controller: controller,
crossAxisCount: 4,
items: List<int>.generate(100, (int index) => index),
sizeBuilder: (int index) {
if (index == 1 && size != null) {
return size;
}
return null;
},
builder: true,
)),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(200.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(400.0, 300.0),
);
controller.jumpTo(10000);
await tester.pumpAndSettle();
expect(find.widgetWithText(Container, '0'), findsNothing);
// change child size from 100 to 30.
size = 30;
await setStateBoilerplate(tester);
controller.jumpTo(100);
await tester.pumpAndSettle();
expect(find.widgetWithText(Container, '0'), findsNothing);
expect(find.widgetWithText(Container, '1'), findsNothing);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, -100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, -100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(200.0, -70.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(200.0, 30.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(0.0, 200.0),
);
});
// code coverage: [RenderSliverMasonryGrid]
// if (data.layoutOffset < -precisionErrorTolerance)
testWidgets('insert child out of viewport test',
(WidgetTester tester) async {
final ScrollController controller = ScrollController();
double size;
await tester.pumpWidget(
materialAppBoilerplate(
child: SliverMasonryTestPage(
controller: controller,
crossAxisCount: 4,
items: List<int>.generate(100, (int index) => index),
sizeBuilder: (int index) {
if (index == 0 && size != null) {
return size;
}
return null;
},
setState: (_SliverMasonryTestPageState state) {
state._items.insert(0, 999);
},
builder: true,
)),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(200.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(400.0, 300.0),
);
controller.jumpTo(10000);
await tester.pumpAndSettle();
expect(find.widgetWithText(Container, '0'), findsNothing);
// insert 999 into 0 index with size 500.0.
size = 500;
await setStateBoilerplate(tester);
controller.jumpTo(0);
await tester.pumpAndSettle();
expect(
tester.getTopLeft(find.widgetWithText(Container, '999')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(200.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(200.0, 300.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(400.0, 300.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(0.0, 500.0),
);
});
testWidgets('remove child out of viewport test',
(WidgetTester tester) async {
final ScrollController controller = ScrollController();
int removeIndex;
await tester.pumpWidget(
materialAppBoilerplate(
child: SliverMasonryTestPage(
controller: controller,
crossAxisCount: 4,
items: List<int>.generate(100, (int index) => index),
sizeBuilder: (int index) {
if (removeIndex != null && index >= removeIndex) {
return (((index + 1) % 4) + 1) * 100.0;
}
return null;
},
setState: (_SliverMasonryTestPageState state) {
state._items.remove(removeIndex);
},
builder: true,
)),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(200.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(400.0, 300.0),
);
controller.jumpTo(10000);
await tester.pumpAndSettle();
expect(find.widgetWithText(Container, '0'), findsNothing);
// remove index 3.
removeIndex = 3;
await setStateBoilerplate(tester);
controller.jumpTo(0);
await tester.pumpAndSettle();
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(600.0, 100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(200.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '8')),
const Offset(0.0, 300.0),
);
});
testWidgets('test with other slivers', (WidgetTester tester) async {
await tester.pumpWidget(
materialAppBoilerplate(
child: SliverMasonryTestPage(
crossAxisCount: 2,
headerSlivers: <Widget>[
SliverToBoxAdapter(
child: Container(
height: 200.0,
color: Colors.red,
),
)
],
footSlivers: <Widget>[
SliverToBoxAdapter(
child: Container(
height: 200.0,
color: Colors.blue,
),
)
],
),
textDirection: TextDirection.ltr),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(0.0, 250.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3.Who scream')),
const Offset(400.0, 270.0),
);
});
testWidgets('insert other slivers test', (WidgetTester tester) async {
final ScrollController controller = ScrollController();
await tester.pumpWidget(
materialAppBoilerplate(
child: SliverMasonryTestPage(
controller: controller,
crossAxisCount: 2,
headerSlivers: <Widget>[
SliverToBoxAdapter(
child: Container(
height: 200.0,
color: Colors.red,
),
)
],
footSlivers: <Widget>[
SliverToBoxAdapter(
child: Container(
height: 200.0,
color: Colors.blue,
),
)
],
setState: (_SliverMasonryTestPageState state) {
state._headerSlivers.add(
SliverToBoxAdapter(
child: Container(
height: 200.0,
color: Colors.yellow,
),
),
);
},
),
textDirection: TextDirection.ltr),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(0.0, 250.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3.Who scream')),
const Offset(400.0, 270.0),
);
controller.jumpTo(10000);
await tester.pumpAndSettle();
expect(
find.widgetWithText(Container, "0.He'd have you all unravel at the"),
findsOneWidget);
await setStateBoilerplate(tester);
controller.jumpTo(0);
await tester.pumpAndSettle();
expect(
tester.getTopLeft(find.widgetWithText(
Container, "0.He'd have you all unravel at the")),
const Offset(0.0, 400.0),
);
});
});
}
SliverWaterfallFlow masonryGridBoilerplate({
int crossAxisCount = 4,
double crossAxisSpacing = 0.0,
double mainAxisSpacing = 0.0,
double maxCrossAxisExtent,
List<int> items,
double Function(int index) sizeBuilder,
}) {
final SliverWaterfallFlowDelegate delegate = maxCrossAxisExtent != null
? SliverWaterfallFlowDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: maxCrossAxisExtent,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
)
: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
);
return SliverWaterfallFlow(
gridDelegate: delegate,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
child: Text(
'${items[index]}',
),
height: sizeBuilder?.call(index) ??
((index % crossAxisCount) + 1) * 100.0,
);
},
childCount: items.length,
),
);
}
SliverWaterfallFlow sliverMasonryGridWithChildrenBoilerplate({
int crossAxisCount = 2,
double crossAxisSpacing = 0.0,
double mainAxisSpacing = 0.0,
double maxCrossAxisExtent,
}) {
final List<Widget> children = <Widget>[
Container(
padding: const EdgeInsets.all(8),
child: const Text("0.He'd have you all unravel at the"),
color: Colors.teal[100],
height: 50.0,
),
Container(
padding: const EdgeInsets.all(8),
child: const Text('1.Heed not the rabble'),
color: Colors.teal[200],
height: 70.0,
),
Container(
padding: const EdgeInsets.all(8),
child: const Text('2.Sound of screams but the'),
color: Colors.teal[300],
height: 90.0,
),
Container(
padding: const EdgeInsets.all(8),
child: const Text('3.Who scream'),
color: Colors.teal[400],
height: 60.0,
),
Container(
padding: const EdgeInsets.all(8),
child: const Text('4.Revolution is coming...'),
color: Colors.teal[500],
height: 80.0,
),
Container(
padding: const EdgeInsets.all(8),
child: const Text('5.Revolution, they...'),
color: Colors.teal[600],
height: 100.0,
),
];
return maxCrossAxisExtent != null
? SliverWaterfallFlow.extent(
maxCrossAxisExtent: maxCrossAxisExtent,
crossAxisSpacing: crossAxisSpacing,
mainAxisSpacing: mainAxisSpacing,
children: children,
)
: SliverWaterfallFlow.count(
crossAxisCount: crossAxisCount,
crossAxisSpacing: crossAxisSpacing,
mainAxisSpacing: mainAxisSpacing,
children: children,
);
}
Widget customScrollViewBoilerplate({
List<Widget> headerSlivers,
List<Widget> footSlivers,
SliverWaterfallFlow sliverMasonryGrid,
ScrollController controller,
}) {
return CustomScrollView(
controller: controller,
slivers: <Widget>[
if (headerSlivers != null) ...headerSlivers,
sliverMasonryGrid,
if (footSlivers != null) ...footSlivers,
],
);
}
Widget materialAppBoilerplate({
Widget child,
TextDirection textDirection = TextDirection.ltr,
}) {
return MaterialApp(
home: Directionality(
textDirection: textDirection,
child: MediaQuery(
data: const MediaQueryData(size: Size(800.0, 600.0)),
child: Material(
child: child,
),
),
),
);
}
class SliverMasonryTestPage extends StatefulWidget {
const SliverMasonryTestPage({
this.crossAxisCount = 4,
this.mainAxisSpacing = 0.0,
this.crossAxisSpacing = 0.0,
this.textDirection = TextDirection.ltr,
this.maxCrossAxisExtent,
this.items,
this.controller,
this.setState,
this.builder = false,
this.headerSlivers,
this.footSlivers,
this.sizeBuilder,
});
final int crossAxisCount;
final double mainAxisSpacing;
final double crossAxisSpacing;
final TextDirection textDirection;
final double maxCrossAxisExtent;
final List<int> items;
final ScrollController controller;
final void Function(_SliverMasonryTestPageState setState) setState;
final bool builder;
final List<Widget> headerSlivers;
final List<Widget> footSlivers;
final double Function(int index) sizeBuilder;
@override
_SliverMasonryTestPageState createState() => _SliverMasonryTestPageState();
}
class _SliverMasonryTestPageState extends State<SliverMasonryTestPage> {
int _crossAxisCount;
double _crossAxisSpacing;
double _mainAxisSpacing;
TextDirection _textDirection;
double _maxCrossAxisExtent;
List<Widget> _headerSlivers;
List<Widget> _footSlivers;
List<int> _items;
@override
void initState() {
_crossAxisCount = widget.crossAxisCount;
_mainAxisSpacing = widget.mainAxisSpacing;
_crossAxisSpacing = widget.crossAxisSpacing;
_textDirection = widget.textDirection;
_maxCrossAxisExtent = widget.maxCrossAxisExtent;
_items = widget.items;
_headerSlivers = widget.headerSlivers;
_footSlivers = widget.footSlivers;
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Directionality(
textDirection: _textDirection,
child: customScrollViewBoilerplate(
controller: widget.controller,
headerSlivers: _headerSlivers,
footSlivers: _footSlivers,
sliverMasonryGrid: widget.builder
? masonryGridBoilerplate(
crossAxisCount: _crossAxisCount,
mainAxisSpacing: _mainAxisSpacing,
crossAxisSpacing: _crossAxisSpacing,
maxCrossAxisExtent: _maxCrossAxisExtent,
items: _items,
sizeBuilder: widget.sizeBuilder,
)
: sliverMasonryGridWithChildrenBoilerplate(
crossAxisCount: _crossAxisCount,
mainAxisSpacing: _mainAxisSpacing,
crossAxisSpacing: _crossAxisSpacing,
maxCrossAxisExtent: _maxCrossAxisExtent,
),
)),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
widget.setState?.call(this);
});
},
child: const Icon(Icons.add),
),
);
}
}
Future<void> setStateBoilerplate(WidgetTester tester) async {
await tester.tap(find.byType(FloatingActionButton));
await tester.pumpAndSettle();
}
const List<String> kStates = <String>[
'Alabama',
'Alaska',
'Arizona',
'Arkansas',
'California',
'Colorado',
'Connecticut',
'Delaware',
'Florida',
'Georgia',
'Hawaii',
'Idaho',
'Illinois',
'Indiana',
'Iowa',
'Kansas',
'Kentucky',
'Louisiana',
'Maine',
'Maryland',
'Massachusetts',
'Michigan',
'Minnesota',
'Mississippi',
'Missouri',
'Montana',
'Nebraska',
'Nevada',
'New Hampshire',
'New Jersey',
'New Mexico',
'New York',
'North Carolina',
'North Dakota',
'Ohio',
'Oklahoma',
'Oregon',
'Pennsylvania',
'Rhode Island',
'South Carolina',
'South Dakota',
'Tennessee',
'Texas',
'Utah',
'Vermont',
'Virginia',
'Washington',
'West Virginia',
'Wisconsin',
'Wyoming',
];
// @dart = 2.8
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:waterfall_flow/waterfall_flow.dart';
void main() {
group('WaterfallFlow', () {
testWidgets('the size of each child', (WidgetTester tester) async {
await tester.pumpWidget(materialAppBoilerplate(
child: masonryGridViewWithChildrenBoilerplate(crossAxisCount: 2),
textDirection: TextDirection.ltr,
));
expect(
tester.getSize(find.widgetWithText(
Container, "0.He'd have you all unravel at the")),
const Size(400.0, 50.0),
);
expect(
tester.getSize(find.widgetWithText(Container, '1.Heed not the rabble')),
const Size(400.0, 70.0),
);
expect(
tester.getSize(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Size(400.0, 90.0),
);
expect(
tester.getSize(find.widgetWithText(Container, '3.Who scream')),
const Size(400.0, 60.0),
);
expect(
tester.getSize(
find.widgetWithText(Container, '4.Revolution is coming...')),
const Size(400.0, 80.0),
);
expect(
tester.getSize(find.widgetWithText(Container, '5.Revolution, they...')),
const Size(400.0, 100.0),
);
});
testWidgets('the position of each child at TextDirection.ltr',
(WidgetTester tester) async {
await tester.pumpWidget(materialAppBoilerplate(
child: masonryGridViewWithChildrenBoilerplate(crossAxisCount: 2),
textDirection: TextDirection.ltr,
));
expect(
tester.getTopLeft(find.widgetWithText(
Container, "0.He'd have you all unravel at the")),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '1.Heed not the rabble')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(0.0, 50.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3.Who scream')),
const Offset(400.0, 70.0),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '4.Revolution is coming...')),
const Offset(400.0, 130.0),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '5.Revolution, they...')),
const Offset(0.0, 140.0),
);
});
testWidgets('the position of each child at TextDirection.rtl',
(WidgetTester tester) async {
await tester.pumpWidget(
materialAppBoilerplate(
child: masonryGridViewWithChildrenBoilerplate(crossAxisCount: 2),
textDirection: TextDirection.rtl),
);
expect(
tester.getTopLeft(find.widgetWithText(
Container, "0.He'd have you all unravel at the")),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '1.Heed not the rabble')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(400.0, 50.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3.Who scream')),
const Offset(0.0, 70.0),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '4.Revolution is coming...')),
const Offset(0.0, 130.0),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '5.Revolution, they...')),
const Offset(400.0, 140.0),
);
});
testWidgets('crossAxisCount change test', (WidgetTester tester) async {
int crossAxisCount = 2;
await tester.pumpWidget(
materialAppBoilerplate(
child: MasonryTestPage(
crossAxisCount: crossAxisCount,
setState: (_MasonryTestPageState state) {
state._crossAxisCount = crossAxisCount;
},
),
textDirection: TextDirection.ltr),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(0.0, 50.0),
);
crossAxisCount = 4;
await setStateBoilerplate(tester);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(400.0, 0.0),
);
});
testWidgets('crossAxisSpacing change test', (WidgetTester tester) async {
double crossAxisSpacing = 10;
await tester.pumpWidget(
materialAppBoilerplate(
child: MasonryTestPage(
crossAxisSpacing: crossAxisSpacing,
crossAxisCount: 2,
setState: (_MasonryTestPageState state) {
state._crossAxisSpacing = crossAxisSpacing;
},
),
textDirection: TextDirection.ltr),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(0.0, 50.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3.Who scream')),
const Offset(405.0, 70.0),
);
crossAxisSpacing = 20;
await setStateBoilerplate(tester);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(0.0, 50.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3.Who scream')),
const Offset(410.0, 70.0),
);
});
testWidgets('mainAxisSpacing change test', (WidgetTester tester) async {
double mainAxisSpacing = 10;
await tester.pumpWidget(
materialAppBoilerplate(
child: MasonryTestPage(
mainAxisSpacing: mainAxisSpacing,
crossAxisCount: 2,
setState: (_MasonryTestPageState state) {
state._mainAxisSpacing = mainAxisSpacing;
},
),
textDirection: TextDirection.ltr),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(0.0, 60.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3.Who scream')),
const Offset(400.0, 80.0),
);
mainAxisSpacing = 20;
await setStateBoilerplate(tester);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(0.0, 70.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3.Who scream')),
const Offset(400.0, 90.0),
);
});
testWidgets('maxCrossAxisExtent change test', (WidgetTester tester) async {
double maxCrossAxisExtent = 400;
await tester.pumpWidget(
materialAppBoilerplate(
child: MasonryTestPage(
maxCrossAxisExtent: maxCrossAxisExtent,
setState: (_MasonryTestPageState state) {
state._maxCrossAxisExtent = maxCrossAxisExtent;
},
),
textDirection: TextDirection.ltr),
);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(0.0, 50.0),
);
maxCrossAxisExtent = 200;
await setStateBoilerplate(tester);
expect(
tester.getTopLeft(
find.widgetWithText(Container, '2.Sound of screams but the')),
const Offset(400.0, 0.0),
);
});
testWidgets('Vertical are primary by default', (WidgetTester tester) async {
final WaterfallFlow view = WaterfallFlow(
scrollDirection: Axis.vertical,
gridDelegate: const SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
);
expect(view.primary, isTrue);
});
testWidgets('with controllers are non-primary by default',
(WidgetTester tester) async {
final WaterfallFlow view = WaterfallFlow(
controller: ScrollController(),
scrollDirection: Axis.vertical,
gridDelegate: const SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
);
expect(view.primary, isFalse);
});
testWidgets('sets PrimaryScrollController when primary',
(WidgetTester tester) async {
final ScrollController primaryScrollController = ScrollController();
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: PrimaryScrollController(
controller: primaryScrollController,
child: WaterfallFlow(
primary: true,
gridDelegate:
const SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
crossAxisCount: 3),
),
),
),
);
final Scrollable scrollable = tester.widget(find.byType(Scrollable));
expect(scrollable.controller, primaryScrollController);
});
testWidgets('dismiss keyboard onDrag test', (WidgetTester tester) async {
final List<FocusNode> focusNodes =
List<FocusNode>.generate(50, (int i) => FocusNode());
await tester.pumpWidget(textFieldBoilerplate(
child: WaterfallFlow(
padding: const EdgeInsets.all(0),
gridDelegate:
const SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
children: focusNodes.map((FocusNode focusNode) {
return Container(
height: 50,
color: Colors.green,
child: TextField(
focusNode: focusNode,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
)),
);
}).toList(),
),
));
final Finder finder = find.byType(TextField).first;
final TextField textField = tester.widget(finder);
await tester.showKeyboard(finder);
expect(textField.focusNode.hasFocus, isTrue);
await tester.drag(finder, const Offset(0.0, -40.0));
await tester.pumpAndSettle();
expect(textField.focusNode.hasFocus, isFalse);
});
testWidgets('count dismiss keyboard onDrag test',
(WidgetTester tester) async {
final List<FocusNode> focusNodes =
List<FocusNode>.generate(50, (int i) => FocusNode());
await tester.pumpWidget(textFieldBoilerplate(
child: WaterfallFlow.count(
padding: const EdgeInsets.all(0),
crossAxisCount: 2,
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
children: focusNodes.map((FocusNode focusNode) {
return Container(
height: 50,
color: Colors.green,
child: TextField(
focusNode: focusNode,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
)),
);
}).toList(),
),
));
final Finder finder = find.byType(TextField).first;
final TextField textField = tester.widget(finder);
await tester.showKeyboard(finder);
expect(textField.focusNode.hasFocus, isTrue);
await tester.drag(finder, const Offset(0.0, -40.0));
await tester.pumpAndSettle();
expect(textField.focusNode.hasFocus, isFalse);
});
testWidgets('extent dismiss keyboard onDrag test',
(WidgetTester tester) async {
final List<FocusNode> focusNodes =
List<FocusNode>.generate(50, (int i) => FocusNode());
await tester.pumpWidget(textFieldBoilerplate(
child: WaterfallFlow.extent(
padding: const EdgeInsets.all(0),
maxCrossAxisExtent: 300,
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
children: focusNodes.map((FocusNode focusNode) {
return Container(
height: 50,
color: Colors.green,
child: TextField(
focusNode: focusNode,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
)),
);
}).toList(),
),
));
final Finder finder = find.byType(TextField).first;
final TextField textField = tester.widget(finder);
await tester.showKeyboard(finder);
expect(textField.focusNode.hasFocus, isTrue);
await tester.drag(finder, const Offset(0.0, -40.0));
await tester.pumpAndSettle();
expect(textField.focusNode.hasFocus, isFalse);
});
testWidgets('dismiss keyboard manual test', (WidgetTester tester) async {
final List<FocusNode> focusNodes =
List<FocusNode>.generate(50, (int i) => FocusNode());
await tester.pumpWidget(textFieldBoilerplate(
child: WaterfallFlow(
padding: const EdgeInsets.all(0),
gridDelegate:
const SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.manual,
children: focusNodes.map((FocusNode focusNode) {
return Container(
height: 50,
color: Colors.green,
child: TextField(
focusNode: focusNode,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
)),
);
}).toList(),
),
));
final Finder finder = find.byType(TextField).first;
final TextField textField = tester.widget(finder);
await tester.showKeyboard(finder);
expect(textField.focusNode.hasFocus, isTrue);
await tester.drag(finder, const Offset(0.0, -40.0));
await tester.pumpAndSettle();
expect(textField.focusNode.hasFocus, isTrue);
});
testWidgets('count dismiss keyboard manual test',
(WidgetTester tester) async {
final List<FocusNode> focusNodes =
List<FocusNode>.generate(50, (int i) => FocusNode());
await tester.pumpWidget(textFieldBoilerplate(
child: WaterfallFlow.count(
padding: const EdgeInsets.all(0),
crossAxisCount: 2,
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.manual,
children: focusNodes.map((FocusNode focusNode) {
return Container(
height: 50,
color: Colors.green,
child: TextField(
focusNode: focusNode,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
)),
);
}).toList(),
),
));
final Finder finder = find.byType(TextField).first;
final TextField textField = tester.widget(finder);
await tester.showKeyboard(finder);
expect(textField.focusNode.hasFocus, isTrue);
await tester.drag(finder, const Offset(0.0, -40.0));
await tester.pumpAndSettle();
expect(textField.focusNode.hasFocus, isTrue);
});
testWidgets('extend dismiss keyboard manual test',
(WidgetTester tester) async {
final List<FocusNode> focusNodes =
List<FocusNode>.generate(50, (int i) => FocusNode());
await tester.pumpWidget(textFieldBoilerplate(
child: WaterfallFlow.extent(
padding: const EdgeInsets.all(0),
maxCrossAxisExtent: 300,
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.manual,
children: focusNodes.map((FocusNode focusNode) {
return Container(
height: 50,
color: Colors.green,
child: TextField(
focusNode: focusNode,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
)),
);
}).toList(),
),
));
final Finder finder = find.byType(TextField).first;
final TextField textField = tester.widget(finder);
await tester.showKeyboard(finder);
expect(textField.focusNode.hasFocus, isTrue);
await tester.drag(finder, const Offset(0.0, -40.0));
await tester.pumpAndSettle();
expect(textField.focusNode.hasFocus, isTrue);
});
testWidgets('itemCount change test', (WidgetTester tester) async {
final ScrollController controller = ScrollController();
int itemCount = 100;
await tester.pumpWidget(
materialAppBoilerplate(
child: MasonryTestPage(
controller: controller,
crossAxisCount: 4,
items: List<int>.generate(itemCount, (int index) => index),
setState: (_MasonryTestPageState state) {
state._items = List<int>.generate(itemCount, (int index) => index);
},
builder: true,
)),
);
expect(find.widgetWithText(Container, '0'), findsOneWidget);
controller.jumpTo(10000);
await tester.pumpAndSettle();
itemCount = 0;
await setStateBoilerplate(tester);
expect(find.widgetWithText(Container, '0'), findsNothing);
expect(controller.offset, 0.0);
itemCount = 100;
await setStateBoilerplate(tester);
expect(find.widgetWithText(Container, '3'), findsOneWidget);
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 100.0),
);
});
testWidgets('items removeRange test', (WidgetTester tester) async {
final ScrollController controller = ScrollController();
int start;
int end;
await tester.pumpWidget(
materialAppBoilerplate(
child: MasonryTestPage(
controller: controller,
crossAxisCount: 4,
items: List<int>.generate(100, (int index) => index),
setState: (_MasonryTestPageState state) {
state._items.removeRange(start, end);
},
builder: true,
)),
);
expect(find.widgetWithText(Container, '0'), findsOneWidget);
controller.jumpTo(10000);
await tester.pumpAndSettle();
start = 40;
end = 60;
await setStateBoilerplate(tester);
controller.jumpTo(0);
await tester.pumpAndSettle();
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 100.0),
);
});
// code coverage: [RenderSliverMasonryGrid]
// if (data.layoutOffset < -precisionErrorTolerance)
testWidgets('the child who out of viewport change big test',
(WidgetTester tester) async {
final ScrollController controller = ScrollController();
double size;
await tester.pumpWidget(
materialAppBoilerplate(
child: MasonryTestPage(
controller: controller,
crossAxisCount: 4,
items: List<int>.generate(100, (int index) => index),
sizeBuilder: (int index) {
if (index == 1 && size != null) {
return size;
}
return null;
},
builder: true,
)),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(200.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(400.0, 300.0),
);
controller.jumpTo(10000);
await tester.pumpAndSettle();
expect(find.widgetWithText(Container, '0'), findsNothing);
// change child size from 200 to 500.
size = 500;
await setStateBoilerplate(tester);
controller.jumpTo(0);
await tester.pumpAndSettle();
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(400.0, 300.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(0.0, 400.0),
);
});
// code coverage: [RenderSliverMasonryGrid]
// if (earliestUsefulChild == null) {
testWidgets('the child who out of viewport change small sroll into 0 test',
(WidgetTester tester) async {
final ScrollController controller = ScrollController();
double size;
await tester.pumpWidget(
materialAppBoilerplate(
child: MasonryTestPage(
controller: controller,
crossAxisCount: 4,
items: List<int>.generate(100, (int index) => index),
sizeBuilder: (int index) {
if (index == 0 && size != null) {
return size;
}
return null;
},
builder: true,
)),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(200.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(400.0, 300.0),
);
controller.jumpTo(10000);
await tester.pumpAndSettle();
expect(find.widgetWithText(Container, '0'), findsNothing);
// change child size from 100 to 30.
size = 30;
await setStateBoilerplate(tester);
controller.jumpTo(0);
await tester.pumpAndSettle();
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 30.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 130.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(200.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(400.0, 300.0),
);
});
// code coverage: [RenderSliverMasonryGrid]
// if (earliestUsefulChild == null) {
testWidgets(
'the child who out of viewport change small sroll into 100 test',
(WidgetTester tester) async {
final ScrollController controller = ScrollController();
double size;
await tester.pumpWidget(
materialAppBoilerplate(
child: MasonryTestPage(
controller: controller,
crossAxisCount: 4,
items: List<int>.generate(100, (int index) => index),
sizeBuilder: (int index) {
if (index == 1 && size != null) {
return size;
}
return null;
},
builder: true,
)),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(200.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(400.0, 300.0),
);
controller.jumpTo(10000);
await tester.pumpAndSettle();
expect(find.widgetWithText(Container, '0'), findsNothing);
// change child size from 100 to 30.
size = 30;
await setStateBoilerplate(tester);
controller.jumpTo(100);
await tester.pumpAndSettle();
expect(find.widgetWithText(Container, '0'), findsNothing);
expect(find.widgetWithText(Container, '1'), findsNothing);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, -100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, -100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(200.0, -70.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(200.0, 30.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(0.0, 200.0),
);
});
// code coverage: [RenderSliverMasonryGrid]
// if (data.layoutOffset < -precisionErrorTolerance)
testWidgets('insert child out of viewport test',
(WidgetTester tester) async {
final ScrollController controller = ScrollController();
double size;
await tester.pumpWidget(
materialAppBoilerplate(
child: MasonryTestPage(
controller: controller,
crossAxisCount: 4,
items: List<int>.generate(100, (int index) => index),
sizeBuilder: (int index) {
if (index == 0 && size != null) {
return size;
}
return null;
},
setState: (_MasonryTestPageState state) {
state._items.insert(0, 999);
},
builder: true,
)),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(200.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(400.0, 300.0),
);
controller.jumpTo(10000);
await tester.pumpAndSettle();
expect(find.widgetWithText(Container, '0'), findsNothing);
// insert 999 into 0 index with size 500.0.
size = 500;
await setStateBoilerplate(tester);
controller.jumpTo(0);
await tester.pumpAndSettle();
expect(
tester.getTopLeft(find.widgetWithText(Container, '999')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(200.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(200.0, 300.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(400.0, 300.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(0.0, 500.0),
);
});
testWidgets('remove child out of viewport test',
(WidgetTester tester) async {
final ScrollController controller = ScrollController();
int removeIndex;
await tester.pumpWidget(
materialAppBoilerplate(
child: MasonryTestPage(
controller: controller,
crossAxisCount: 4,
items: List<int>.generate(100, (int index) => index),
sizeBuilder: (int index) {
if (removeIndex != null && index >= removeIndex) {
return (((index + 1) % 4) + 1) * 100.0;
}
return null;
},
setState: (_MasonryTestPageState state) {
state._items.remove(removeIndex);
},
builder: true,
)),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '3')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(0.0, 100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(200.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(400.0, 300.0),
);
controller.jumpTo(10000);
await tester.pumpAndSettle();
expect(find.widgetWithText(Container, '0'), findsNothing);
// remove index 3.
removeIndex = 3;
await setStateBoilerplate(tester);
controller.jumpTo(0);
await tester.pumpAndSettle();
expect(
tester.getTopLeft(find.widgetWithText(Container, '0')),
const Offset(0.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '1')),
const Offset(200.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '2')),
const Offset(400.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '4')),
const Offset(600.0, 0.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '5')),
const Offset(0.0, 100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '6')),
const Offset(600.0, 100.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '7')),
const Offset(200.0, 200.0),
);
expect(
tester.getTopLeft(find.widgetWithText(Container, '8')),
const Offset(0.0, 300.0),
);
});
});
}
Widget masonryGridViewWithChildrenBoilerplate({
int crossAxisCount = 2,
double crossAxisSpacing = 0.0,
double mainAxisSpacing = 0.0,
double maxCrossAxisExtent,
ScrollController controller,
}) {
final List<Widget> children = <Widget>[
Container(
padding: const EdgeInsets.all(8),
child: const Text("0.He'd have you all unravel at the"),
color: Colors.teal[100],
height: 50.0,
),
Container(
padding: const EdgeInsets.all(8),
child: const Text('1.Heed not the rabble'),
color: Colors.teal[200],
height: 70.0,
),
Container(
padding: const EdgeInsets.all(8),
child: const Text('2.Sound of screams but the'),
color: Colors.teal[300],
height: 90.0,
),
Container(
padding: const EdgeInsets.all(8),
child: const Text('3.Who scream'),
color: Colors.teal[400],
height: 60.0,
),
Container(
padding: const EdgeInsets.all(8),
child: const Text('4.Revolution is coming...'),
color: Colors.teal[500],
height: 80.0,
),
Container(
padding: const EdgeInsets.all(8),
child: const Text('5.Revolution, they...'),
color: Colors.teal[600],
height: 100.0,
),
];
if (maxCrossAxisExtent != null) {
return WaterfallFlow.extent(
maxCrossAxisExtent: maxCrossAxisExtent,
crossAxisSpacing: crossAxisSpacing,
mainAxisSpacing: mainAxisSpacing,
children: children,
controller: controller,
);
}
return WaterfallFlow.count(
crossAxisCount: crossAxisCount,
crossAxisSpacing: crossAxisSpacing,
mainAxisSpacing: mainAxisSpacing,
children: children,
controller: controller,
);
}
Widget masonryGridViewBuilderBoilerplate({
int crossAxisCount = 4,
double crossAxisSpacing = 0.0,
double mainAxisSpacing = 0.0,
double maxCrossAxisExtent,
ScrollController controller,
List<int> items,
double Function(int index) sizeBuilder,
}) {
final SliverWaterfallFlowDelegate delegate = maxCrossAxisExtent != null
? SliverWaterfallFlowDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: maxCrossAxisExtent,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
)
: SliverWaterfallFlowDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
mainAxisSpacing: mainAxisSpacing,
crossAxisSpacing: crossAxisSpacing,
);
return WaterfallFlow.builder(
gridDelegate: delegate,
itemBuilder: (BuildContext context, int index) {
return Container(
child: Text(
'${items[index]}',
),
height:
sizeBuilder?.call(index) ?? ((index % crossAxisCount) + 1) * 100.0,
);
},
itemCount: items.length,
controller: controller,
);
}
Widget materialAppBoilerplate(
{Widget child, TextDirection textDirection = TextDirection.ltr}) {
return MaterialApp(
home: Directionality(
textDirection: textDirection,
child: MediaQuery(
data: const MediaQueryData(size: Size(800.0, 600.0)),
child: Material(
child: child,
),
),
),
);
}
Widget textFieldBoilerplate({Widget child}) {
return MaterialApp(
home: Localizations(
locale: const Locale('en', 'US'),
delegates: <LocalizationsDelegate<dynamic>>[
WidgetsLocalizationsDelegate(),
MaterialLocalizationsDelegate(),
],
child: Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(size: Size(800.0, 600.0)),
child: Center(
child: Material(
child: child,
),
),
),
),
),
);
}
class MasonryTestPage extends StatefulWidget {
const MasonryTestPage({
this.crossAxisCount = 4,
this.mainAxisSpacing = 0.0,
this.crossAxisSpacing = 0.0,
this.textDirection = TextDirection.ltr,
this.maxCrossAxisExtent,
this.items,
this.controller,
this.setState,
this.builder = false,
this.sizeBuilder,
});
final int crossAxisCount;
final double mainAxisSpacing;
final double crossAxisSpacing;
final TextDirection textDirection;
final double maxCrossAxisExtent;
final List<int> items;
final ScrollController controller;
final void Function(_MasonryTestPageState setState) setState;
final bool builder;
final double Function(int index) sizeBuilder;
@override
_MasonryTestPageState createState() => _MasonryTestPageState();
}
class _MasonryTestPageState extends State<MasonryTestPage> {
int _crossAxisCount;
double _crossAxisSpacing;
double _mainAxisSpacing;
TextDirection _textDirection;
double _maxCrossAxisExtent;
List<int> _items;
@override
void initState() {
_crossAxisCount = widget.crossAxisCount;
_mainAxisSpacing = widget.mainAxisSpacing;
_crossAxisSpacing = widget.crossAxisSpacing;
_textDirection = widget.textDirection;
_maxCrossAxisExtent = widget.maxCrossAxisExtent;
_items = widget.items;
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Directionality(
textDirection: _textDirection,
child: widget.builder
? masonryGridViewBuilderBoilerplate(
crossAxisCount: _crossAxisCount,
mainAxisSpacing: _mainAxisSpacing,
crossAxisSpacing: _crossAxisSpacing,
maxCrossAxisExtent: _maxCrossAxisExtent,
controller: widget.controller,
items: _items,
sizeBuilder: widget.sizeBuilder,
)
: masonryGridViewWithChildrenBoilerplate(
crossAxisCount: _crossAxisCount,
mainAxisSpacing: _mainAxisSpacing,
crossAxisSpacing: _crossAxisSpacing,
maxCrossAxisExtent: _maxCrossAxisExtent,
controller: widget.controller,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
widget.setState?.call(this);
});
},
child: const Icon(Icons.add),
),
);
}
}
Future<void> setStateBoilerplate(WidgetTester tester) async {
await tester.tap(find.byType(FloatingActionButton));
await tester.pumpAndSettle();
}
class MaterialLocalizationsDelegate
extends LocalizationsDelegate<MaterialLocalizations> {
@override
bool isSupported(Locale locale) => true;
@override
Future<MaterialLocalizations> load(Locale locale) =>
DefaultMaterialLocalizations.load(locale);
@override
bool shouldReload(MaterialLocalizationsDelegate old) => false;
}
class WidgetsLocalizationsDelegate
extends LocalizationsDelegate<WidgetsLocalizations> {
@override
bool isSupported(Locale locale) => true;
@override
Future<WidgetsLocalizations> load(Locale locale) =>
DefaultWidgetsLocalizations.load(locale);
@override
bool shouldReload(WidgetsLocalizationsDelegate old) => false;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment