diff --git a/case_study/memory_leak/.gitignore b/case_study/memory_leak/.gitignore
new file mode 100644
index 00000000000..eb15c3d27ca
--- /dev/null
+++ b/case_study/memory_leak/.gitignore
@@ -0,0 +1,10 @@
+.DS_Store
+.atom/
+.idea
+.packages
+.pub/
+build/
+ios/.generated/
+packages
+pubspec.lock
+.flutter-plugins
diff --git a/case_study/memory_leak/README.md b/case_study/memory_leak/README.md
new file mode 100644
index 00000000000..acaef7c56ff
--- /dev/null
+++ b/case_study/memory_leak/README.md
@@ -0,0 +1,12 @@
+# Using Tab Bar
+
+A material design widget that displays a horizontal row of tabs.
+
+Read [[Documentation](https://docs.flutter.io/flutter/material/TabBar-class.html)] [[Material Design Spec](https://material.io/guidelines/components/tabs.html)]
+
+
+
+
+## Getting Started
+
+For help getting started with Flutter, view online [documentation](http://flutter.io/).
\ No newline at end of file
diff --git a/case_study/memory_leak/android/.gitignore b/case_study/memory_leak/android/.gitignore
new file mode 100644
index 00000000000..1658458c924
--- /dev/null
+++ b/case_study/memory_leak/android/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+GeneratedPluginRegistrant.java
diff --git a/case_study/memory_leak/android/app/build.gradle b/case_study/memory_leak/android/app/build.gradle
new file mode 100644
index 00000000000..f440d926bd6
--- /dev/null
+++ b/case_study/memory_leak/android/app/build.gradle
@@ -0,0 +1,52 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withInputStream { stream ->
+ localProperties.load(stream)
+ }
+}
+
+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 "github.nisrulz.usingtabs"
+ 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 {
+ androidTestImplementation 'com.android.support:support-annotations:28.0.0'
+ androidTestImplementation 'com.android.support.test:runner:1.0.2'
+ androidTestImplementation 'com.android.support.test:rules:1.0.2'
+}
diff --git a/case_study/memory_leak/android/app/src/main/AndroidManifest.xml b/case_study/memory_leak/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000000..380e1dd9ca0
--- /dev/null
+++ b/case_study/memory_leak/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/case_study/memory_leak/android/app/src/main/java/github/nisrulz/usingtabs/MainActivity.java b/case_study/memory_leak/android/app/src/main/java/github/nisrulz/usingtabs/MainActivity.java
new file mode 100644
index 00000000000..602d66c418e
--- /dev/null
+++ b/case_study/memory_leak/android/app/src/main/java/github/nisrulz/usingtabs/MainActivity.java
@@ -0,0 +1,14 @@
+package github.nisrulz.usingtabs;
+
+import android.os.Bundle;
+
+import io.flutter.app.FlutterActivity;
+import io.flutter.plugins.GeneratedPluginRegistrant;
+
+public class MainActivity extends FlutterActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ GeneratedPluginRegistrant.registerWith(this);
+ }
+}
diff --git a/case_study/memory_leak/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/case_study/memory_leak/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000000..db77bb4b7b0
Binary files /dev/null and b/case_study/memory_leak/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/case_study/memory_leak/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/case_study/memory_leak/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000000..17987b79bb8
Binary files /dev/null and b/case_study/memory_leak/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/case_study/memory_leak/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/case_study/memory_leak/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000000..09d4391482b
Binary files /dev/null and b/case_study/memory_leak/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/case_study/memory_leak/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/case_study/memory_leak/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000000..d5f1c8d34e7
Binary files /dev/null and b/case_study/memory_leak/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/case_study/memory_leak/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/case_study/memory_leak/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000000..4d6372eebdb
Binary files /dev/null and b/case_study/memory_leak/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/case_study/memory_leak/android/build.gradle b/case_study/memory_leak/android/build.gradle
new file mode 100644
index 00000000000..759b28648ee
--- /dev/null
+++ b/case_study/memory_leak/android/build.gradle
@@ -0,0 +1,27 @@
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.2.1'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/case_study/memory_leak/android/gradle/wrapper/gradle-wrapper.jar b/case_study/memory_leak/android/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000000..13372aef5e2
Binary files /dev/null and b/case_study/memory_leak/android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/case_study/memory_leak/android/gradle/wrapper/gradle-wrapper.properties b/case_study/memory_leak/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000000..bae529009b7
--- /dev/null
+++ b/case_study/memory_leak/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#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.6-all.zip
diff --git a/case_study/memory_leak/android/gradlew b/case_study/memory_leak/android/gradlew
new file mode 100755
index 00000000000..9d82f789151
--- /dev/null
+++ b/case_study/memory_leak/android/gradlew
@@ -0,0 +1,160 @@
+#!/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 "$@"
diff --git a/case_study/memory_leak/android/gradlew.bat b/case_study/memory_leak/android/gradlew.bat
new file mode 100644
index 00000000000..8a0b282aa68
--- /dev/null
+++ b/case_study/memory_leak/android/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/case_study/memory_leak/android/settings.gradle b/case_study/memory_leak/android/settings.gradle
new file mode 100644
index 00000000000..115da6cb4f4
--- /dev/null
+++ b/case_study/memory_leak/android/settings.gradle
@@ -0,0 +1,15 @@
+include ':app'
+
+def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
+
+def plugins = new Properties()
+def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
+if (pluginsFile.exists()) {
+ pluginsFile.withInputStream { stream -> plugins.load(stream) }
+}
+
+plugins.each { name, path ->
+ def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
+ include ":$name"
+ project(":$name").projectDir = pluginDirectory
+}
diff --git a/case_study/memory_leak/demo_img.gif b/case_study/memory_leak/demo_img.gif
new file mode 100644
index 00000000000..5fbe21d7adf
Binary files /dev/null and b/case_study/memory_leak/demo_img.gif differ
diff --git a/case_study/memory_leak/flutter_01.png b/case_study/memory_leak/flutter_01.png
new file mode 100644
index 00000000000..3825d6b574c
Binary files /dev/null and b/case_study/memory_leak/flutter_01.png differ
diff --git a/case_study/memory_leak/ios/.gitignore b/case_study/memory_leak/ios/.gitignore
new file mode 100644
index 00000000000..38864eed23e
--- /dev/null
+++ b/case_study/memory_leak/ios/.gitignore
@@ -0,0 +1,41 @@
+.idea/
+.vagrant/
+.sconsign.dblite
+.svn/
+
+.DS_Store
+*.swp
+profile
+
+DerivedData/
+build/
+GeneratedPluginRegistrant.h
+GeneratedPluginRegistrant.m
+
+*.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/App.framework
+/Flutter/Flutter.framework
+/Flutter/Generated.xcconfig
+/ServiceDefinitions.json
+
+Pods/
diff --git a/case_study/memory_leak/ios/Flutter/AppFrameworkInfo.plist b/case_study/memory_leak/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 00000000000..6c2de8086bc
--- /dev/null
+++ b/case_study/memory_leak/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,30 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ MinimumOSVersion
+ 8.0
+
+
diff --git a/case_study/memory_leak/ios/Flutter/Debug.xcconfig b/case_study/memory_leak/ios/Flutter/Debug.xcconfig
new file mode 100644
index 00000000000..592ceee85b8
--- /dev/null
+++ b/case_study/memory_leak/ios/Flutter/Debug.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/case_study/memory_leak/ios/Flutter/Release.xcconfig b/case_study/memory_leak/ios/Flutter/Release.xcconfig
new file mode 100644
index 00000000000..592ceee85b8
--- /dev/null
+++ b/case_study/memory_leak/ios/Flutter/Release.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/case_study/memory_leak/ios/Runner.xcodeproj/project.pbxproj b/case_study/memory_leak/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 00000000000..b4f28532edf
--- /dev/null
+++ b/case_study/memory_leak/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,424 @@
+// !$*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 */; };
+ 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
+ 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
+ 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
+ 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB31CF90195004384FC /* Generated.xcconfig */; };
+ 9740EEBB1CF902C7004384FC /* app.flx in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB71CF902C7004384FC /* app.flx */; };
+ 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
+ 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
+ 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
+ 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+ 9740EEB71CF902C7004384FC /* app.flx */ = {isa = PBXFileReference; lastKnownFileType = file; name = app.flx; path = Flutter/app.flx; sourceTree = ""; };
+ 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
+ 3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB71CF902C7004384FC /* app.flx */,
+ 3B80C3931E831B6300D905FE /* App.framework */,
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEBA1CF902C7004384FC /* Flutter.framework */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ CF3B75C9A7D2FA2A4C99F110 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
+ 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 97C146F11CF9000F007C117D /* Supporting Files */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ 97C146F11CF9000F007C117D /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146F21CF9000F007C117D /* main.m */,
+ );
+ name = "Supporting Files";
+ sourceTree = "";
+ };
+/* 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 = 0830;
+ ORGANIZATIONNAME = "The Chromium Authors";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ 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 = (
+ 9740EEBB1CF902C7004384FC /* app.flx in Resources */,
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 9740EEB51CF90195004384FC /* Generated.xcconfig in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 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\" 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 = (
+ 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
+ 97C146F31CF9000F007C117D /* main.m 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 = "";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = 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_OBJC_ROOT_CLASS = YES_ERROR;
+ 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 = 8.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = 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_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ARCHS = arm64;
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ 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 = github.nisrulz.usingTabs;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ARCHS = arm64;
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ 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 = github.nisrulz.usingTabs;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147031CF9000F007C117D /* Debug */,
+ 97C147041CF9000F007C117D /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147061CF9000F007C117D /* Debug */,
+ 97C147071CF9000F007C117D /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/case_study/memory_leak/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/case_study/memory_leak/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000000..1d526a16ed0
--- /dev/null
+++ b/case_study/memory_leak/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/case_study/memory_leak/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/case_study/memory_leak/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 00000000000..1c958078819
--- /dev/null
+++ b/case_study/memory_leak/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/case_study/memory_leak/ios/Runner.xcworkspace/contents.xcworkspacedata b/case_study/memory_leak/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000000..1d526a16ed0
--- /dev/null
+++ b/case_study/memory_leak/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/case_study/memory_leak/ios/Runner/AppDelegate.h b/case_study/memory_leak/ios/Runner/AppDelegate.h
new file mode 100644
index 00000000000..cf210d213f2
--- /dev/null
+++ b/case_study/memory_leak/ios/Runner/AppDelegate.h
@@ -0,0 +1,6 @@
+#import
+#import
+
+@interface AppDelegate : FlutterAppDelegate
+
+@end
diff --git a/case_study/memory_leak/ios/Runner/AppDelegate.m b/case_study/memory_leak/ios/Runner/AppDelegate.m
new file mode 100644
index 00000000000..112becd13b3
--- /dev/null
+++ b/case_study/memory_leak/ios/Runner/AppDelegate.m
@@ -0,0 +1,12 @@
+#include "AppDelegate.h"
+#include "GeneratedPluginRegistrant.h"
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+ [GeneratedPluginRegistrant registerWithRegistry:self];
+ // Override point for customization after application launch.
+ return [super application:application didFinishLaunchingWithOptions:launchOptions];
+}
+
+@end
diff --git a/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000000..d22f10b2ab6
--- /dev/null
+++ b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,116 @@
+{
+ "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"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 00000000000..28c6bf03016
Binary files /dev/null and b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 00000000000..2ccbfd967d9
Binary files /dev/null and b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 00000000000..f091b6b0bca
Binary files /dev/null and b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 00000000000..4cde12118dd
Binary files /dev/null and b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 00000000000..d0ef06e7edb
Binary files /dev/null and b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 00000000000..dcdc2306c28
Binary files /dev/null and b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 00000000000..2ccbfd967d9
Binary files /dev/null and b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 00000000000..c8f9ed8f5ce
Binary files /dev/null and b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 00000000000..a6d6b8609df
Binary files /dev/null and b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 00000000000..a6d6b8609df
Binary files /dev/null and b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 00000000000..75b2d164a5a
Binary files /dev/null and b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 00000000000..c4df70d39da
Binary files /dev/null and b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 00000000000..6a84f41e14e
Binary files /dev/null and b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 00000000000..d0e1f585360
Binary files /dev/null and b/case_study/memory_leak/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/case_study/memory_leak/ios/Runner/Base.lproj/LaunchScreen.storyboard b/case_study/memory_leak/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 00000000000..ebf48f60397
--- /dev/null
+++ b/case_study/memory_leak/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/case_study/memory_leak/ios/Runner/Base.lproj/Main.storyboard b/case_study/memory_leak/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 00000000000..f3c28516fb3
--- /dev/null
+++ b/case_study/memory_leak/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/case_study/memory_leak/ios/Runner/Info.plist b/case_study/memory_leak/ios/Runner/Info.plist
new file mode 100644
index 00000000000..2c9f3e55726
--- /dev/null
+++ b/case_study/memory_leak/ios/Runner/Info.plist
@@ -0,0 +1,49 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ using_tabs
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+
+
diff --git a/case_study/memory_leak/ios/Runner/main.m b/case_study/memory_leak/ios/Runner/main.m
new file mode 100644
index 00000000000..0ccc450011c
--- /dev/null
+++ b/case_study/memory_leak/ios/Runner/main.m
@@ -0,0 +1,9 @@
+#import
+#import
+#import "AppDelegate.h"
+
+int main(int argc, char * argv[]) {
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ }
+}
diff --git a/case_study/memory_leak/lib/logging.dart b/case_study/memory_leak/lib/logging.dart
new file mode 100644
index 00000000000..1602a4c7f59
--- /dev/null
+++ b/case_study/memory_leak/lib/logging.dart
@@ -0,0 +1,125 @@
+import 'dart:async';
+import 'package:intl/intl.dart';
+
+
+class Logging {
+ Logging() {
+ model = TimeModel(this)..start();
+ }
+
+ TimeModel model;
+
+ static Logging _theLogging;
+
+ static Logging get logging {
+ _theLogging ??= Logging();
+ return _theLogging;
+ }
+
+ final List _logs = [];
+
+ void add(String entry) {
+ final TimeStamp newTimeStamp = TimeStamp.record(DateTime.now());
+
+ _logs.add('[${model.log.length}] : ${newTimeStamp.time}] $entry');
+ }
+
+ List get logs => _logs;
+}
+
+class TimeStamp {
+ TimeStamp()
+ : time = '',
+ date = '',
+ meridiem = '';
+
+ TimeStamp.record(DateTime now) {
+ time = currentTime.format(now);
+ date = currentDate.format(now);
+ meridiem = currentMeridiem.format(now);
+ }
+
+ String time;
+ String date;
+ String meridiem;
+
+ DateFormat currentTime = DateFormat('h:mm:ss', 'en_US');
+ DateFormat currentDate = DateFormat('EEEE, MMM d', 'en_US');
+ DateFormat currentMeridiem = DateFormat('aaa', 'en_US');
+}
+
+class TimeModel {
+ TimeModel(this._logging);
+
+ Logging _logging;
+
+ List log = [];
+
+ String _time = '';
+ String _date = '';
+ String _meridiem = '';
+ Timer _clockUpdateTimer;
+ DateTime now = DateTime.now();
+
+ /// Start updating.
+ void start() {
+ log.add(TimeStamp());
+ _updateLog();
+ _clockUpdateTimer = Timer.periodic(
+ const Duration(milliseconds: 100),
+ (_) => _updateLog(),
+ );
+ }
+
+ /// Stop updating.
+ void stop() {
+ _clockUpdateTimer?.cancel();
+ _clockUpdateTimer = null;
+ }
+
+ /// The current time in the ambient format.
+ String get time => _time;
+
+ /// The current date in the ambient format.
+ String get date => _date;
+
+ /// The current meridiem in the ambient format.
+ String get meridiem => _meridiem;
+
+ String get partOfDay {
+ if (now.hour < 5) {
+ return 'night';
+ }
+ if (now.hour < 12) {
+ return 'morning';
+ }
+ if (now.hour < 12 + 5) {
+ return 'afternoon';
+ }
+ if (now.hour < 12 + 8) {
+ return 'evening';
+ }
+ return 'night';
+ }
+
+ void _updateLog() {
+ now = DateTime.now();
+ final _year = now.year;
+
+ /// Due to a bug, need to verify the date has the current year before
+ /// returning a date and time.
+ if (_year < 2019) {
+ return;
+ }
+
+ final TimeStamp newTimeStamp = TimeStamp.record(now);
+
+ log.add(newTimeStamp);
+
+ if (newTimeStamp.time != log.last.time ||
+ newTimeStamp.date != log.last.date ||
+ newTimeStamp.meridiem != log.last.meridiem) {
+ _logging.add('${newTimeStamp.time} idle...');
+ }
+ }
+}
diff --git a/case_study/memory_leak/lib/main.dart b/case_study/memory_leak/lib/main.dart
new file mode 100644
index 00000000000..eda1f04acf0
--- /dev/null
+++ b/case_study/memory_leak/lib/main.dart
@@ -0,0 +1,86 @@
+import 'package:flutter/material.dart';
+
+import 'tabs/http_data.dart';
+import 'tabs/logger.dart';
+import 'tabs/settings.dart';
+
+void main() {
+ runApp(MaterialApp(
+ // Title
+ title: 'Memory Leak',
+ // Home
+ home: MyHome()));
+}
+
+class MyHome extends StatefulWidget {
+ @override
+ MyHomeState createState() => MyHomeState();
+}
+
+/// Setup Tabs
+class MyHomeState extends State with SingleTickerProviderStateMixin {
+ // Create a tab controller
+ TabController controller;
+
+ Settings settings;
+
+ @override
+ void initState() {
+ super.initState();
+
+ // Initialize the Tab Controller
+ controller = TabController(length: 3, vsync: this);
+ }
+
+ @override
+ void dispose() {
+ // Dispose of the Tab Controller
+ controller.dispose();
+ super.dispose();
+ }
+
+ TabBar getTabBar() {
+ return TabBar(
+ tabs: [
+ Tab(
+ // set icon to the tab
+ icon: Icon(Icons.wifi),
+ ),
+ Tab(
+ icon: Icon(Icons.build),
+ ),
+ Tab(
+ icon: Icon(Icons.bug_report),
+ ),
+ ],
+ // setup the controller
+ controller: controller,
+ );
+ }
+
+ TabBarView getTabBarView(var tabs) {
+ return TabBarView(
+ // Add tabs as widgets
+ children: tabs,
+ // Set the controller
+ controller: controller,
+ );
+ }
+
+ /// Setup the tabs.
+ @override
+ Widget build(BuildContext context) {
+ settings = Settings();
+ return Scaffold(
+ // Appbar
+ appBar: AppBar(
+ // Title
+ title: const Text('Memory Leak'),
+ // Set the background color of the App Bar
+ backgroundColor: Colors.blue,
+ // Set the bottom property of the Appbar to include a Tab Bar
+ bottom: getTabBar()),
+ // Set the TabBar view as the body of the Scaffold
+ body: getTabBarView([MyGetHttpData(), settings, Logger()]));
+ }
+}
diff --git a/case_study/memory_leak/lib/restful_servers.dart b/case_study/memory_leak/lib/restful_servers.dart
new file mode 100644
index 00000000000..0972a477f3c
--- /dev/null
+++ b/case_study/memory_leak/lib/restful_servers.dart
@@ -0,0 +1,187 @@
+/// All Restful Servers are defined here.
+
+import 'tabs/settings.dart';
+
+/// All servers with RestfulAPI implement this base.
+abstract class RestfulAPI {
+ RestfulAPI(this.previous);
+
+ RestfulAPI previous;
+ RestfulAPI next;
+
+ String uri();
+
+ dynamic findData(dynamic data);
+
+ String display(dynamic data, int index);
+}
+
+/// StarWars information server.
+class StarWars extends RestfulAPI {
+ StarWars([int index = 1]) : super(currentRestfulAPI) {
+ switch (index) {
+ case 0:
+ _defaultUri = StarWars.filmsUri;
+ break;
+ case 1:
+ _defaultUri = StarWars.peopleUri;
+ break;
+ case 2:
+ _defaultUri = StarWars.planetsUri;
+ break;
+ case 3:
+ _defaultUri = StarWars.speciesUri;
+ break;
+ case 4:
+ _defaultUri = StarWars.starshipsUri;
+ break;
+ case 5:
+ _defaultUri = StarWars.vehiclesUri;
+ break;
+ default:
+ _defaultUri = StarWars.peopleUri;
+ }
+ }
+
+ static const peopleUri = 'https://swapi.co/api/people';
+ static const vehiclesUri = 'https://swapi.co/api/vehicles';
+ static const starshipsUri = 'https://swapi.co/api/starships';
+ static const planetsUri = 'https://swapi.co/api/planets';
+ static const filmsUri = 'https://swapi.co/api/films';
+ static const speciesUri = 'https://swapi.co/api/species';
+
+ String _defaultUri;
+
+ @override
+ String uri() => _defaultUri;
+
+ @override
+ dynamic findData(dynamic data) => data['results'];
+
+ @override
+ String display(dynamic data, int index) {
+ final isFilm = _defaultUri == StarWars.filmsUri;
+ return data == null ? '' : data[index][isFilm ? 'title' : 'name'];
+ }
+}
+
+/// CitiBike NYCA single public API that shows location, status and current
+/// availability for all stations in the New York City bike sharing imitative.
+class CitiBikesNYC extends RestfulAPI {
+ CitiBikesNYC() : super(currentRestfulAPI);
+
+ static const citiBikesUri =
+ 'https://feeds.citibikenyc.com/stations/stations.json';
+
+ @override
+ String uri() => citiBikesUri;
+
+ @override
+ dynamic findData(dynamic data) => data['stationBeanList'];
+
+ @override
+ String display(dynamic data, int index) =>
+ data == null ? '' : data[index]['stationName'];
+}
+
+class CityInformation {
+ CityInformation(this.name);
+
+ String name;
+ int size = -1;
+ String state = '???';
+}
+
+/// openewathermap APIs
+///
+/// Docs on Restful APIs https://openweathermap.org/current#data
+///
+/// City IDs are found in these files: http://bulk.openweathermap.org/sample/
+///
+/// Find a particular city use grep e.g.,
+/// > grep -A 10 -B 10 -i "Kansas City" city.list.json
+///
+/// APPID is ca0dbbe6d72c4d9e8c829abb4a534c16 DevTools_memoryLeaks
+///
+/// Subscribe for free OpenWeatherMap then create an appid using:
+/// https://home.openweathermap.org/api_keys
+class OpenWeatherMapAPI extends RestfulAPI {
+ OpenWeatherMapAPI() : super(currentRestfulAPI);
+
+ static const _baseUrl = 'http://api.openweathermap.org/data/2.5/group?id=';
+ static const _unitsOption = '&units=imperial';
+ static const _appidOption = '&appid=ca0dbbe6d72c4d9e8c829abb4a534c16';
+
+ static const _cityIds = {
+ 'Seattle': 5809844,
+ 'Atlanta': 4180439,
+ 'Portland': 4720131,
+ 'Chicago': 4887398,
+ 'Orlando': 4167147,
+ 'San Francisco': 5391959,
+ 'San Jose': 5392171,
+ 'Phoenix': 5131135,
+ 'Denver': 4853799,
+ 'St. Louis': 6157004,
+ 'Houston': 4430529,
+ 'Dallas': 5722064,
+ 'Mobile': 4076598,
+ 'Richmond': 5780388,
+ 'Hartford': 5255628,
+ 'Detroit': 4990729,
+ 'Minneapolis': 4275586,
+ 'Cleveland': 5248933,
+ 'Harrisburg': 5228340,
+ 'Bangor': 5244626,
+ 'Nashville': 4245376,
+ 'Boston': 4183849,
+ 'Las Vegas': 5475433,
+ 'Kansas City': 4273837,
+ };
+
+ CityInformation firstCity;
+ CityInformation secondCity;
+
+ String _cityIdsList({int initialStart = -1, int count = 20}) {
+ final StringBuffer buff = StringBuffer();
+
+ final int start = initialStart == -1 ? randomSeed() : initialStart;
+ firstCity = CityInformation(_cityIds.keys.toList()[start]);
+
+ secondCity = firstCity;
+
+ final ids = _cityIds.values.toList();
+ for (var index = start; index < count; index++) {
+ if (index != start) buff.write(',');
+ buff.write('${ids[index]}');
+ }
+
+ return buff.toString();
+ }
+
+ // Index starting from 0..3
+ static int randomSeed() => DateTime.now().second % 4;
+
+ String get uri20Cities =>
+ '$_baseUrl${_cityIdsList()}$_unitsOption$_appidOption';
+
+ @override
+ String uri() => uri20Cities;
+
+ @override
+ dynamic findData(dynamic data) => data['list']; // weather group
+
+ static String cityName(dynamic data, int index) => '${data[index]['name']}';
+
+ static String temperature(dynamic data, int index) =>
+ '${data[index]['main']['temp']}';
+
+ static String weather(dynamic data, int index) =>
+ '${data[index]['weather'][0]['main']}';
+
+ @override
+ String display(dynamic data, int index) =>
+ '${OpenWeatherMapAPI.cityName(data, index)} '
+ '${OpenWeatherMapAPI.temperature(data, index)} '
+ '${OpenWeatherMapAPI.weather(data, index)}';
+}
\ No newline at end of file
diff --git a/case_study/memory_leak/lib/tabs/http_data.dart b/case_study/memory_leak/lib/tabs/http_data.dart
new file mode 100644
index 00000000000..a6e36506084
--- /dev/null
+++ b/case_study/memory_leak/lib/tabs/http_data.dart
@@ -0,0 +1,125 @@
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:flutter/material.dart';
+import 'package:http/http.dart' as http;
+
+import '../logging.dart';
+import '../restful_servers.dart';
+import 'settings.dart';
+
+
+/// Create a stateful widget
+class MyGetHttpData extends StatefulWidget {
+ @override
+ MyGetHttpDataState createState() => MyGetHttpDataState();
+}
+
+// Create the state for our stateful widget
+class MyGetHttpDataState extends State {
+ MyGetHttpDataState() {
+ api = computeUri();
+ currentRestfulAPI?.next = api;
+ currentRestfulAPI = api;
+ }
+
+ final Logging logs = Logging.logging;
+
+ RestfulAPI api;
+ List data;
+
+ RestfulAPI computeUri() {
+ switch (restfulApi) {
+ case 'Weather':
+ return OpenWeatherMapAPI();
+ case 'NYC Bike Sharing':
+ return CitiBikesNYC();
+ case 'StarWars Films':
+ return StarWars(0);
+ case 'StarWars People':
+ return StarWars(1);
+ case 'StarWars Planets':
+ return StarWars(2);
+ case 'StarWars Species':
+ return StarWars(3);
+ case 'StarWars Starships':
+ return StarWars(4);
+ case 'StarWars Vehicles':
+ return StarWars(5);
+ default:
+ return StarWars();
+ }
+ }
+
+ // Function to get the JSON data
+ Future getJSONData() async {
+ logs.add(api.uri());
+
+ final startTime = DateTime.now();
+
+ final response = await http.get(
+ // Encode the url
+ Uri.encodeFull(api.uri()),
+ // Only accept JSON response
+ headers: {'Accept': 'application/json'});
+
+ logs.add('Packet received on ${response.headers['date']} '
+ 'content-size ${response.contentLength} bytes '
+ 'elapsed time ${DateTime.now().difference(startTime)}');
+
+ // To modify the state of the app, use this method
+ setState(() {
+ // Get the JSON data
+ final dataConvertedToJSON = json.decode(response.body);
+ // Extract the required part and assign it to the global variable named data
+ data = api.findData(dataConvertedToJSON);
+ });
+
+ return 'Successful';
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Retrieve JSON Data via HTTP GET'),
+ ),
+ // Create a Listview and load the data when available
+ body: ListView.builder(
+ itemCount: data == null ? 0 : data.length,
+ itemBuilder: (BuildContext context, int index) {
+ return Container(
+ child: Center(
+ child: Column(
+ // Stretch the cards in horizontal axis
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Card(
+ child: Container(
+ child: Text(
+ // Read the name field value and set it in the Text widget
+ api?.display(data, index),
+
+ // set some style to text
+ style: TextStyle(
+ fontSize: 20.0, color: Colors.lightBlueAccent),
+ ),
+ // added padding
+ padding: const EdgeInsets.all(15.0),
+ ),
+ )
+ ],
+ )),
+ );
+ }),
+ );
+ }
+
+ @override
+ void initState() {
+ super.initState();
+
+ // Call the getJSONData() method when the app initializes
+ getJSONData();
+ }
+}
diff --git a/case_study/memory_leak/lib/tabs/logger.dart b/case_study/memory_leak/lib/tabs/logger.dart
new file mode 100644
index 00000000000..d0cf6bef23d
--- /dev/null
+++ b/case_study/memory_leak/lib/tabs/logger.dart
@@ -0,0 +1,129 @@
+import 'package:flutter/material.dart';
+
+import '../logging.dart';
+
+class Logger extends StatelessWidget {
+ final Logging _logging = Logging.logging;
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: 'Logger',
+ theme: ThemeData(
+ primaryColor: Colors.blue, accentColor: Colors.lightBlue),
+ home: LogEntries(_logging),
+ );
+ }
+}
+
+class LogEntries extends StatefulWidget {
+ const LogEntries(this._logging);
+
+ final Logging _logging;
+
+ @override
+ State createState() => LoggingState(_logging);
+}
+
+class LoggingState extends State {
+ LoggingState(this._logging);
+
+ Logging _logging;
+ final _saved = List();
+ final _biggerFont = const TextStyle(fontSize: 18.0);
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Infinite List'),
+ centerTitle: true,
+ actions: [
+ IconButton(icon: Icon(Icons.list)/*, onPressed: _pushSaved*/),
+ ],
+ ),
+ body: _buildSuggestions(),
+ );
+ }
+
+ void _pushSaved() {
+ Navigator.of(context).push(
+ MaterialPageRoute(
+ builder: (context) {
+ final tiles = _saved.map(
+ (itemValue) {
+ return ListTile(
+ title: Text(
+ itemValue,
+ style: _biggerFont,
+ ),
+ );
+ },
+ );
+ final divided = ListTile
+ .divideTiles(
+ context: context,
+ tiles: tiles,
+ )
+ .toList();
+
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('Saved lists'),
+ ),
+ body: ListView(children: divided),
+ );
+ },
+ ),
+ );
+ }
+
+ Widget _buildRow(String logEntry) {
+ return ListTile(
+ title: Text(
+ 'LOG: $logEntry',
+ style: _biggerFont,
+ ),
+ trailing: Icon(
+// alreadySaved ? Icons.favorite : Icons.favorite_border,
+// color: alreadySaved ? Colors.red : null,
+ Icons.favorite_border,
+ color: null,
+ ),
+ onTap: () {
+ setState(() {
+ _saved.add('LOG: $logEntry');
+ });
+ },
+ );
+ }
+
+ Widget _buildSuggestions() {
+ return ListView.builder(
+ padding: const EdgeInsets.all(16.0),
+ // The itemBuilder callback is called once per suggested word pairing,
+ // and places each suggestion into a ListTile row.
+ // For even rows, the function adds a ListTile row for the word pairing.
+ // For odd rows, the function adds a Divider widget to visually
+ // separate the entries. Note that the divider may be difficult
+ // to see on smaller devices.
+ itemBuilder: (context, i) {
+ // Add a one-pixel-high divider widget before each row in theListView.
+ if (i.isOdd) return const Divider();
+
+ // The syntax "i ~/ 2" divides i by 2 and returns an integer result.
+ // For example: 1, 2, 3, 4, 5 becomes 0, 1, 1, 2, 2.
+ // This calculates the actual number of word pairings in the ListView,
+ // minus the divider widgets.
+ final index = i ~/ 2;
+ if (index < _logging.logs.length)
+ return _buildRow(_logging.logs[index]);
+/*
+ // Emits Idle... lots of them every 100ms.
+ // TOOD(terry): UI needs to appear sluggish clue to look for leaks, etc.
+ else
+ return _buildRow('Idle...');
+*/
+ });
+ }
+}
\ No newline at end of file
diff --git a/case_study/memory_leak/lib/tabs/settings.dart b/case_study/memory_leak/lib/tabs/settings.dart
new file mode 100644
index 00000000000..017c342ebb0
--- /dev/null
+++ b/case_study/memory_leak/lib/tabs/settings.dart
@@ -0,0 +1,64 @@
+import 'package:flutter/material.dart';
+
+import '../logging.dart';
+import '../restful_servers.dart';
+
+class Settings extends StatefulWidget {
+ Settings() : restfulRoot = currentRestfulAPI;
+
+ final Logging logs = Logging.logging;
+
+ static SettingsState state;
+
+ final RestfulAPI restfulRoot;
+
+ @override
+ SettingsState createState() {
+ state = SettingsState();
+ return state;
+ }
+
+ SettingsState get currentState => state;
+}
+
+/// Which Restful Server is selected.
+String restfulApi = 'StarWars People';
+
+RestfulAPI currentRestfulAPI;
+
+class SettingsState extends State {
+ Map values = {
+ 'NYC Bike Sharing': false,
+ 'StarWars Films': false,
+ 'StarWars People': false,
+ 'StarWars Planets': false,
+ 'StarWars Species': false,
+ 'StarWars Starships': false,
+ 'StarWars Vehicles': false,
+ 'Weather': false,
+ };
+
+ final Logging logs = Logging.logging;
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(title: const Text('Restful Servers')),
+ body: ListView(
+ children: values.keys.map((String key) {
+ return RadioListTile(
+ title: Text(key),
+ value: key,
+ groupValue: restfulApi,
+ onChanged: (String value) {
+ setState(() {
+ restfulApi = value;
+ logs.add('Settings is now $restfulApi');
+ });
+ },
+ );
+ }).toList(),
+ ),
+ );
+ }
+}
diff --git a/case_study/memory_leak/pubspec.yaml b/case_study/memory_leak/pubspec.yaml
new file mode 100644
index 00000000000..d16e76b5dc9
--- /dev/null
+++ b/case_study/memory_leak/pubspec.yaml
@@ -0,0 +1,59 @@
+name: memory_leak
+description: A new Flutter project.
+
+dependencies:
+ flutter:
+ sdk: flutter
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+ intl: ^0.15.7
+ http: ^0.12.0
+
+# For information on the generic Dart part of this file, see the
+# following page: https://www.dartlang.org/tools/pub/pubspec
+
+# The following section is specific to Flutter.
+flutter:
+
+ # The following line ensures that the Material Icons font is
+ # included with your application, so that you can use the icons in
+ # the 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.io/assets-and-images/.
+
+ # To add assets from package dependencies, first ensure the asset
+ # is in the lib/ directory of the dependency. Then,
+ # refer to the asset with a path prefixed with
+ # `packages/PACKAGE_NAME/`. The `lib/` is implied, do not
+ # include `lib/` in the asset path.
+ #
+ # Here is an example:
+ #
+ # assets:
+ # - packages/PACKAGE_NAME/path/to/asset
+
+ # 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
diff --git a/case_study/memory_leak/test/widget_test.dart b/case_study/memory_leak/test/widget_test.dart
new file mode 100644
index 00000000000..e69de29bb2d