Skip to content

Commit

Permalink
Merge pull request #113 from felangel/flutter_bloc_package
Browse files Browse the repository at this point in the history
Add bloc_library example
  • Loading branch information
brianegan committed Mar 2, 2019
2 parents f5acaa5 + e75b68b commit a9339cc
Show file tree
Hide file tree
Showing 100 changed files with 3,088 additions and 9 deletions.
26 changes: 18 additions & 8 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
jobs:
include:

- stage: Unit Testing
language: generic
os: linux
env: All unit and widget tests
before_script:
- sudo apt-get install -y --no-install-recommends lib32stdc++6 libstdc++6 > /dev/null

# - git clone https://github.com/flutter/flutter.git -b stable
# - flutter precache
# - git clone https://github.com/flutter/flutter.git -b stable
# - flutter precache

# install pre-compiled flutter
- FLUTTER_CHANNEL=stable
Expand Down Expand Up @@ -61,8 +61,8 @@ jobs:
- echo no | avdmanager create avd --force -n test -k "system-images;android-$EMULATOR_API_LEVEL;$ANDROID_ABI"
- $ANDROID_HOME/emulator/emulator -avd test -no-audio -no-window -gpu swiftshader &

# - git clone https://github.com/flutter/flutter.git -b stable
# - flutter precache
# - git clone https://github.com/flutter/flutter.git -b stable
# - flutter precache

# install pre-compiled flutter
- FLUTTER_CHANNEL=stable
Expand Down Expand Up @@ -92,15 +92,15 @@ jobs:
- open /Applications/Xcode.app/Contents/Developer/Applications/Simulator.app
# skip homebrew update
- export HOMEBREW_NO_AUTO_UPDATE=1
# - brew update
# - brew update
- brew install libimobiledevice
- brew install ideviceinstaller
- brew install ios-deploy
- brew install cocoapods || echo 'ignore exit(1)'
- brew link --overwrite cocoapods

# - git clone https://github.com/flutter/flutter.git -b stable
# - flutter precache
# - git clone https://github.com/flutter/flutter.git -b stable
# - flutter precache

# install pre-compiled flutter
- FLUTTER_CHANNEL=stable
Expand All @@ -125,6 +125,16 @@ jobs:
env: built_redux_ios
before_install: *before_install_osx
script: travis_retry ./scripts/ci.sh ./built_redux
- <<: *integration-test-staging
os: linux
env: bloc_library_android
script: travis_retry ./scripts/ci.sh ./bloc_library
- <<: *integration-test-staging
os: osx
osx_image: xcode8.0
env: bloc_library_ios
before_install: *before_install_osx
script: travis_retry ./scripts/ci.sh ./bloc_library
- <<: *integration-test-staging
os: linux
env: firestore_redux_android
Expand Down
71 changes: 71 additions & 0 deletions bloc_library/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Miscellaneous
*.class
*.lock
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/

# IntelliJ related
*.iml
*.ipr
*.iws
.idea/

# Visual Studio Code related
.vscode/

# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.packages
.pub-cache/
.pub/
build/

# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java

# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*

# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
10 changes: 10 additions & 0 deletions bloc_library/.metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# 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: 5391447fae6209bb21a89e6a5a6583cac1af9b4b
channel: beta

project_type: app
42 changes: 42 additions & 0 deletions bloc_library/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# bloc_library sample

This sample makes use of the [bloc](https://pub.dartlang.org/packages/bloc) and [flutter_bloc](https://pub.dartlang.org/packages/flutter_bloc) libraries to manage state.

Check out the [bloc library documentation](https://felangel.github.io/bloc) for more details and tutorials.

## Key Concepts

* Lift State Up
* If a bloc is needed by multiple widgets provide it using `BlocProvider` at the greatest common ancestor
* `Events` are the input to a bloc.
* They are commonly dispatched in response to user interactions such as button presses or lifecycle events like page loads.
* `States` are the output of a bloc and represent a part of your application's state.
* components can be notified of states and redraw portions of themselves based on the current state.
* The change from one state to another is called a `Transition`.
* A `Transition` consists of the current state, the event, and the next state.
* A bloc converts a `Stream` of incoming `Events` into a `Stream` of outgoing `States`

## Updating App State

Every bloc has a `dispatch` method. `Dispatch` takes an event and triggers `mapEventToState`. Dispatch notifies the bloc of a new event which will then trigger a state change.

## Sharing Data between Blocs

In this implementation, the `FilteredTodosBloc` depends on the `TodosBloc` and will update the list of filtered todos in response to changes in the `TodosBloc`.
This approach demonstrates how we can build a reactive application by composing blocs from other blocs. On app start, the `TodosBloc` is hydrated with todos from the repository and from that moment forward, all mutations to todos are executed through the `TodosBloc` which persists the changes asynchronously. The `FilteredTodosBloc` listens for changes in the `TodosBloc` state and will update it's list of filtered todos. The result is, a hierarchy of states with all todos being managed by the `TodosBloc` and a subset of those todos (filtered todos) being managed by the `FilteredTodosBloc`.

## Updating UI

`BlocBuilder` is a Flutter widget which requires a bloc and a builder function. `BlocBuilder` handles building the widget in response to new states. `BlocBuilder` is very similar to `StreamBuilder` but has a simplified API to reduce the amount of boilerplate code needed and is also optimized to avoid unnecessary rebuilds.

## Testing

Generally, this app conforms the "Testing Pyramid": Lots of Unit tests, fewer Widget tests, and fewer integration tests.

* Unit tests
- `Blocs` can easily be tested by mocking dependencies with `Mockito`. `Events` can be dispatched and we can assert that the blocs' state has changed correctly.
* Widget Tests
- Widgets can be tested by passing in fake data and making assertions against the Widget rendered with that data.
* Integration Tests
- Run the app and drive it using flutter_driver `flutter drive --target test_driver/todo_app.dart`.

17 changes: 17 additions & 0 deletions bloc_library/android/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>android</name>
<comment>Project android created by Buildship.</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
</projectDescription>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
connection.project.dir=
eclipse.preferences.version=1
6 changes: 6 additions & 0 deletions bloc_library/android/app/.classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>
23 changes: 23 additions & 0 deletions bloc_library/android/app/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>app</name>
<comment>Project app created by Buildship.</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
</projectDescription>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
connection.project.dir=..
eclipse.preferences.version=1
61 changes: 61 additions & 0 deletions bloc_library/android/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}

def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}

apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
compileSdkVersion 27

lintOptions {
disable 'InvalidPackage'
}

defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.flutterbloc"
minSdkVersion 16
targetSdkVersion 27
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
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.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
39 changes: 39 additions & 0 deletions bloc_library/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.flutterbloc">

<!-- 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="bloc_library"
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>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.flutterbloc;

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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?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>
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions bloc_library/android/app/src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?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>

0 comments on commit a9339cc

Please sign in to comment.