Commit 23c856ca authored by 李增强's avatar 李增强

修改头部可以在指定位置

parents
.DS_Store
.dart_tool/
.packages
.pub/
build/
ios/.generated/
ios/Flutter/Generated.xcconfig
ios/Runner/GeneratedPluginRegistrant.*
*.iml
pubspec.lock
.idea
# 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: a0860f6e87ba4f9031bee4d6f56c08b970606bee
channel: dev
project_type: app
This diff is collapsed.
MIT License
Copyright (c) 2018 Jpeng
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.
This diff is collapsed.
This diff is collapsed.
# 关于自定义指示器
有时,我们可能嫌弃提供指示器不太好看,或者不符合自己要求,需要自己去定义一个属于自己App的指示器。
现提供两种自定义指示器的方法。
## 第一种
假设你要实现的指示器功能不是太过于复杂,可以使用CustomHeader或者CustomFooter,利用SmartRefresher里的onOffsetChange回调可完成一些简单的动画。(1.5.2新增参数回调,你也可以直接用这个实现复杂的动画效果)
```dart
Widget buildHeader(BuildContext context,RefreshStatus mode){
return Center(
child:Text(mode==RefreshStatus.idle?"下拉刷新":mode==RefreshStatus.refreshing?"刷新中...":
mode==RefreshStatus.canRefresh?"可以松手了!":mode==RefreshStatus.completed?"刷新成功!":"刷新失败");
)
}
SmartRefresher(
...
header: CustomHeader(
builder:buildHeader
onOffsetChange:(offset){
//do some ani
}
)
...
)
```
## 第二种
这种方式可能相对上面难说实现起来是相对来说比较复杂的,但通过这种方式可以更好的实现一些比较绚丽的动画。
首先,头部指示器(下拉刷新)有四种风格:Follow,UnFollow,Behind,Front,底部指示器(加载更多)只有一种风格:Follow
先来了解一下这四种风格到底有什么区别?
* Follow: 高度不会变化,而位置会随着列表的移动而移动
* UnFollow: 高度不会变化,位置不变,但不是跟着列表变化,当它完全可以被看见时,不会再跟随列表。
* Behind:高度会随着越界拖动距离变化,不会移动。
* Front: 和前面三种机制不一样,高度不会变动,位置也不会变动。一直卡在列表顶端。
首先,头部指示器继承RefreshIndicator,底部指示器继承LoadIndicator,同时内部已经封装好State(RefreshIndicatorState,LoadIndicatorState)。
这里,你不需要去关心一些问题就是:通过什么去监听指示器offset的变化?如何去让指示器进入某个状态?怎么根据offset来决定是否进入刷新状态?
怎么颁布刷新状态变化逻辑? 上述这些问题你不需要去关心,因为我内部已经处理好这些问题。你只需要关心以下东西:
不同的状态要返回什么布局,根据offset变化来设置一些动画的进度,进入刷新状态前执行什么操作等等。
下面直接以实现一个简单的指示器为例子,这样才能更好的明白怎么设计一个指示器。
现在,猜想我们要实现一个这样的指示器,如下图:
![](arts/custom_header.gif)
拖动时,随着offset的变化而变化图片大小,然后刷新完毕后,调用一个平移动画形成跑过去的效果。
那么,问题来了,我们应该要怎么去实现这样的功能呢?
首先,我准备了一张jpg和一个gif,jpg表示gif第一帧,当然如果你想控制gif,也是有办法的,一个gif够了
定义一个类继承RefreshIndicator(注意material包重名)
```dart
class RunningHeader extends RefreshIndicator {
RunningHeader({@required OnRefresh onRefresh})
: super(
refreshStyle: RefreshStyle.Follow,
height: 80.0,
onRefresh: onRefresh);
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return RunningHeaderState();
}
}
```
如上,RefreshIndicator具体有什么属性,可以看[这里](indicator_attribute.md),利用super(refreshStyle:)可以指定指示器的风格,因为这里我觉得
Follow更适合一点,所以就选Follow了。
接下来RunningHeaderState是重点部分,它继承于RefreshIndicatorState而不是State。内部有一个必须得重写的方法buildContent,用来指定指示器要显示什么,这个我相信绝大数人都能明白,就不细说了。
第一步,如何实现拖动手势过程中人缩小放大?缩小放大肯定是要用ScaleTransition,那么问题又来了,那我怎么去知道当前offset是多少呢?
内部有一个onOffsetChange的回调方法,这个方法触发的时机是任何时候都会触发的,如果你不想在刷新过程中或者布局浮动的状态下触发,你可以利用floating这个属性,这个
属性表示指示器是否占有一个高度的状态,假如占有一个高度,它就能被看到,假如没有,就滚回去隐藏掉了。
```dart
class RunningHeaderState extends RefreshIndicatorState<RunningHeader>
with TickerProviderStateMixin {
AnimationController _scaleAnimation;
void onOffsetChange(){
if (!floating) {
_scaleAnimation.value = offset / 80.0;
}
//call super,will call setState
super.onOffsetChange(offset);
}
Widget buildContent(BuildContext context, RefreshStatus mode){
return ScaleTransition(
child: (mode != RefreshStatus.idle || mode != RefreshStatus.canRefresh)
? Image.asset("images/custom_2.gif")
: Image.asset("images/custom_1.jpg"),
scale: _scaleAnimation,
);
}
}
```
第二步,那个刷新完成之后人跑过去的效果怎么弄?这点需要endRefresh这个方法,这个方法要返回一个Future。注意,这个方法调用,此时的状态是RefreshStatus.completed或者failed
,然后当这个方法执行完毕,此时,floating =false,header就会开始隐藏掉。
```dart
@override
Future<void> endRefresh() {
// TODO: implement endRefresh
return _offsetController.animateTo(1.0).whenComplete(() {});
}
Widget buildContent(BuildContext context, RefreshStatus mode) {
// TODO: implement buildContent
return SlideTransition(
child: ScaleTransition(
child: (mode != RefreshStatus.idle || mode != RefreshStatus.canRefresh)
? Image.asset("images/custom_2.gif")
: Image.asset("images/custom_1.jpg"),
scale: _scaleAnimation,
),
position: offsetTween.animate(_offsetController),
);
}
```
实现到这里,这个指示器基本就差不多完成了。但是,还有一步,就是你那些动画总得要还原到原来的位置吧,总不能还在那个位置停留。下次用户下拉刷新的时候就可能还是那个状态值。
那这里,应该怎么做?内部没有特定方法,但是可以利用RefreshStatus的状态还原为idle后,接着再还原controller里的值,里面有暴露一个resetValue的方法
```dart
@override
void resetValue() {
// TODO: implement handleModeChange
_scaleAnimation.value = 0.0;
_offsetController.value = 0.0;
}
```
这个header就不放到packages中了,因为大多人用不上,只是作为一个例子。[代码](example/lib/other/RunningHeader.dart)
RefreshIndicatorState里一些非常重要的可重写方法和属性
```dart
/*
代表指示器有没有布局的高度,假如占有高度,指示器将会展示在顶部,假如没有,指示器将会滚动回去隐藏掉。
*/
bool floating;
//指示器的状态
RefreshStatus mode;
/*
这个方法里的参数返回一个值,代表指示器可见的距离或者ScrollView顶部越界的距离。
你可以利用这个方法来实现一些漂亮的拖动动画。比如WaterDropHeader里水滴拖动的效果,
就是要依赖到这个函数。最后,调用父类的onOffsetChange可以更新界面。
*/
void onOffsetChange(double offset) ;
// 当指示器状态发生改变时,会回调
void onModeChange(RefreshStatus mode);
/*
这个方法表示即将进入刷新状态时需要执行的操作,返回一个Future。这个方法调用完毕才能进入刷新状态。
*/
Future<void> readyToRefresh();
/**
结束刷新状态时的操作。这个方法是在状态改变为成功或者失败后触发的。
这个方法执行完毕后,指示器会变为无布局(floating = false)状态
*/
Future<void> endRefresh();
// 根据不同的状态,返回不同的内容
Widget buildContent(BuildContext context,RefreshStatus mode);
```
LoadIndicatorState就不展开介绍了,和上面同理,只不过上面的比较多。
## 第三种(1.5.2支持,推荐使用这种方法!)
1.5.2把footer,header转换为Widget限制类型,这样的好处是为了更容易组合指示器来使用,更符合flutter的设计规则,而不是返回一个函数
我们可以把CustomHeader封装在一个StatelessWidget和StatefulWidget里。当我们需要组合其他组件时,可以向上回调函数来使用,参考BeizerHeader。
你也可以组合ClassicHeader来使用,TwoLevelHeader也是基于ClassicHeader来组合使用。CustomHeader里面的参数和上面方法意思一样。
```dart
class XXXXHeader extends StatelessWidget{
Widget build(){
return CustomHeader(
....
);
}
}
```
# About Custom Indicator
## First
Assuming that the indicator function you want to implement is not too complex, you can use CustomHeader or CustomFooter, and use the onOffset Change
callback in Smart Refresher to complete some simple animations.((1.5.2 add new callback in CustomHeader and customFooter,you can use this to implements complex animation))
```dart
Widget buildHeader(BuildContext context,RefreshStatus mode){
.....
}
SmartRefresher(
...
header: CustomHeader(builder:buildHeader,
onOffsetChange:(offset){
//do some ani
}
),
...
)
```
## Second
This method may be relatively difficult to say above to achieve is relatively complex, but through this way can better achieve some of the more gorgeous animation.
First, the head indicator (drop-down refresh) has four styles: Follow, UnFollow, Behind, Front, and the bottom indicator (load more) has only one style: Follow
Let's first understand the difference between these four styles.
* Follow: Height will not change, but position moves as the list moves.
* UnFollow: Height does not change, location does not change, but does not follow the list, when it is fully visible, will not follow the list.
* Behind:Height will vary with the distance across the boundary and will not move.
* Front: Unlike the previous three mechanisms, height and position will not change. It's stuck at the top of the list.
First,header Indicator need to extend RefreshIndicator,footer Indicator need to extend LoadIndicator,At the same time, the State has been encapsulated internally((RefreshIndicatorState,LoadIndicatorState)。)
Here, you don't need to worry about the following questions: how to monitor the offset changes of the indicator, how to get the indicator into a certain state, and how to decide whether to enter a refresh state based on offset?
How to promulgate the refresh state change logic? You don't need to care about these problems, because I have dealt with them internally. You only need to care about the following things:
What layout to return to in different states, how to set the progress of some animations according to offset changes, what operations to perform before entering refresh state, and so on.
Let's take a simple indicator as an example, so that we can better understand how to design an indicator.
Now, suppose we want to implement an indicator like this, as follows:
![](arts/custom_header.gif)
When dragging, change the size of the picture as offset changes, and then after refreshing, call a moving picture to form the effect of running past.
So, the question arises, how should we achieve such a function?
First of all, I prepared a JPG and a gif. JPG represents the first frame of gif. Of course, if you want to control gif, there is a way. A GIF is enough.
Define a class to inherit RefreshIndicator (note the material package rename)
```dart
class RunningHeader extends RefreshIndicator {
RunningHeader({@required OnRefresh onRefresh})
: super(
refreshStyle: RefreshStyle.Follow,
height: 80.0,
onRefresh: onRefresh);
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return RunningHeaderState();
}
}
```
As mentioned above, RefreshIndicator has specific attributes, you can see [here](indicator_attribute.md), and you can specify the style of the indicator by using super (refreshStyle:), because here I think
Follow is more suitable, so I chose Follow.
Next, Running Header State is the key part, which inherits from Refresh Indicator State rather than State. There is a built Content method that must be rewritten inside to specify what the indicator will display, which I believe most people can understand, let alone go into details.
The first step is how to zoom in and out in the process of dragging gestures. Scale Transition must be used to zoom in and out. Then the problem arises again. How can I know the current offset?
There's an onOffsetChange callback method inside, which triggers at any time. If you don't want to trigger during refresh or when the layout is floating, you can use the floating property, which is
Attributes indicate whether the indicator occupies a high state. If it occupies a high state, it can be seen. If it does not, it rolls back and hides.
```dart
class RunningHeaderState extends RefreshIndicatorState<RunningHeader>
with TickerProviderStateMixin {
AnimationController _scaleAnimation;
void onOffsetChange(){
if (!floating) {
_scaleAnimation.value = offset / 80.0;
}
//call super,will call setState
super.onOffsetChange(offset);
}
Widget buildContent(BuildContext context, RefreshStatus mode){
return ScaleTransition(
child: (mode != RefreshStatus.idle || mode != RefreshStatus.canRefresh)
? Image.asset("images/custom_2.gif")
: Image.asset("images/custom_1.jpg"),
scale: _scaleAnimation,
);
}
}
```
The second step is what happens when the refresh is complete. This requires the endRefresh method, which returns to a Future. Note that this method call is in RefreshStatus. completed or failed state
Then when this method is finished, floating = false, the header starts to hide.
```dart
@override
Future<void> endRefresh() {
// TODO: implement endRefresh
return _offsetController.animateTo(1.0).whenComplete(() {});
}
Widget buildContent(BuildContext context, RefreshStatus mode) {
// TODO: implement buildContent
return SlideTransition(
child: ScaleTransition(
child: (mode != RefreshStatus.idle || mode != RefreshStatus.canRefresh)
? Image.asset("images/custom_2.gif")
: Image.asset("images/custom_1.jpg"),
scale: _scaleAnimation,
),
position: offsetTween.animate(_offsetController),
);
}
```
So far, the indicator is almost complete. But one more step is that your animations have to be restored to their original position, and you can't stay there. The next time the user drops down and refreshes, it may still be that state value.
So what should we do here? There is no specific method inside, but you can restore the status of RefreshStatus to idle, and then restore the value in the controller, which exposes a resetValue method.
```dart
@override
void resetValue() {
// TODO: implement handleModeChange
_scaleAnimation.value = 0.0;
_offsetController.value = 0.0;
}
```
This header is not included in packages, because most people don't use it, just as an example.。[Code](example/lib/other/RunningHeader.dart)
the most important api in RefreshIndicatorState
```dart
/*
Represents whether the indicator has a layout height. If it occupies a height, the indicator will be displayed at the top.
If not, the indicator will scroll back and hide.
*/
bool floating;
// the state of RefreshIndicator
RefreshStatus mode;
/*
The parameter in this method returns a value representing the distance visible to the indicator or the distance across the top of the ScrollView.
You can use this method to achieve some beautiful drag animation. For example, the effect of droplet dragging in WaterDropHeader.
It depends on this function. Finally, the onOffsetChange of the parent class is called to update the interface.
*/
void onOffsetChange(double offset) ;
// When the indicator status changes, it calls back
void onModeChange(RefreshStatus mode);
/*
This method represents the operation to be performed when the refresh state is about to enter, and returns a Future. This method cannot be refreshed until it has been called.
*/
Future<void> readyToRefresh();
/**
End the operation when refreshing the status. This method is triggered when the state changes to success or failure.
When this method is executed, the indicator becomes floating = false
*/
Future<void> endRefresh();
// Return different content depending on the RefreshStatus
Widget buildContent(BuildContext context,RefreshStatus mode);
```
LoadIndicatorState does not expand on the introduction, which is similar to the above.
## Third(support after 1.5.2,recommended!)
1.5.2 converts footer and header to Widget restriction type, which has the advantage of making it easier to combine indicators. This design is more in line with flutter's design rules than returning a function
We can encapsulate CustomHeader in a StatelessWidget and StatefulWidget. When we need to combine other components, we can use the callback function up, refer to BeizerHeader.
You can also combine Classic Header and TwoLevel Header based on Classic Header.
```dart
class XXXXHeader extends StatelessWidget{
Widget build(){
return CustomHeader(
....
);
}
}
```
File added
.DS_Store
.dart_tool/
.packages
.pub/
build/
.flutter-plugins
*.iml
pubspec.lock
.idea
\ 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: 7ffcd3d22d7bc1222d53d6d3bb83f59891aac2c2
channel: dev
# demo
A new Flutter project.
## Getting Started
For help getting started with Flutter, view our online
[documentation](https://flutter.io/).
*.iml
*.class
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
GeneratedPluginRegistrant.java
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.")
}
apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 28
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.jpeng.demo"
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
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 {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jpeng.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.jpeng.demo">
<!-- The INTERNET permission is required for development. Specifically,
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"/>
<!-- 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="PullRefreshDemo"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- This keeps the window background of the activity showing
until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen
defined in @style/LaunchTheme). -->
<meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
package com.jpeng.demo;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
}
package com.jpeng.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 when the OS's Dark Mode setting is on -->
<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:colorBackground</item>
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<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>
</resources>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jpeng.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 {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
}
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.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-4.1-all.zip
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}
.idea/
.vagrant/
.sconsign.dblite
.svn/
.DS_Store
*.swp
profile
DerivedData/
build/
GeneratedPluginRegistrant.h
GeneratedPluginRegistrant.m
.generated/
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
xcuserdata
*.moved-aside
*.pyc
*sync/
Icon?
.tags*
/Flutter/app.flx
/Flutter/app.zip
/Flutter/flutter_assets/
/Flutter/App.framework
/Flutter/Flutter.framework
/Flutter/Generated.xcconfig
/ServiceDefinitions.json
Pods/
cede5a498fe37fd7523b504da3df1a2b
\ No newline at end of file
<?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>en</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>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>MinimumOSVersion</key>
<string>8.0</string>
</dict>
</plist>
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
#
# NOTE: This podspec is NOT to be published. It is only used as a local source!
#
Pod::Spec.new do |s|
s.name = 'Flutter'
s.version = '1.0.0'
s.summary = 'High-performance, high-fidelity mobile apps.'
s.description = <<-DESC
Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS.
DESC
s.homepage = 'https://flutter.io'
s.license = { :type => 'MIT' }
s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }
s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s }
s.ios.deployment_target = '8.0'
s.vendored_frameworks = 'Flutter.framework'
end
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
#!/bin/sh
# This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=C:\env\flutter1.22.4"
export "FLUTTER_APPLICATION_PATH=C:\_project_app\pull_to_refresh-1.6.3\example"
export "FLUTTER_TARGET=lib\main.dart"
export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build\ios"
export "OTHER_LDFLAGS=$(inherited) -framework Flutter"
export "FLUTTER_FRAMEWORK_DIR=C:\env\flutter1.22.4\bin\cache\artifacts\engine\ios"
export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1"
export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=false"
export "TREE_SHAKE_ICONS=false"
export "PACKAGE_CONFIG=.packages"
# 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'
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
target 'Runner' do
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
# referring to absolute paths on developers' machines.
system('rm -rf Pods/.symlinks')
system('mkdir -p Pods/.symlinks/plugins')
# Flutter Pods
generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig')
if generated_xcode_build_settings.empty?
puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first."
end
generated_xcode_build_settings.map { |p|
if p[:name] == 'FLUTTER_FRAMEWORK_DIR'
symlink = File.join('Pods', '.symlinks', 'flutter')
File.symlink(File.dirname(p[:path]), symlink)
pod 'Flutter', :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('Pods', '.symlinks', 'plugins', p[:name])
File.symlink(p[:path], symlink)
pod p[:name], :path => File.join(symlink, 'ios')
}
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
end
end
end
PODS:
- Flutter (1.0.0)
DEPENDENCIES:
- Flutter (from `Pods/.symlinks/flutter/ios`)
EXTERNAL SOURCES:
Flutter:
:path: Pods/.symlinks/flutter/ios
SPEC CHECKSUMS:
Flutter: 9d0fac939486c9aba2809b7982dfdbb47a7b0296
PODFILE CHECKSUM: 13dcf421f4da2e937a57e8ba760ed880beae536f
COCOAPODS: 1.5.0
This diff is collapsed.
<?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 = "0910"
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"
language = ""
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"
language = ""
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 = "Release"
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>BuildSystemType</key>
<string>Original</string>
</dict>
</plist>
#import <UIKit/UIKit.h>
#import <Flutter/Flutter.h>
@interface AppDelegate : FlutterAppDelegate
@end
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
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"
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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