Skip to content

Commit

Permalink
Add firestore_redux example
Browse files Browse the repository at this point in the history
  • Loading branch information
kinggolf committed Mar 13, 2018
1 parent c176f90 commit f4dd437
Show file tree
Hide file tree
Showing 107 changed files with 3,835 additions and 1 deletion.
4 changes: 3 additions & 1 deletion README.md
Expand Up @@ -19,7 +19,9 @@ To run the samples, please use the `beta` channel and Dart 1 for now. We'll upda
* [Redux Example](example/redux) - Uses the [Redux](https://pub.dartlang.org/packages/redux) library to manage app state and update Widgets
* [built_redux Example](example/built_redux) - Uses the [built_redux](https://pub.dartlang.org/packages/built_redux) library to enforce immutability and manage app state
* [scoped_model Example](example/scoped_model) - Uses the [scoped_model](https://pub.dartlang.org/packages/scoped_model) library to hold app state and notify Widgets of Updates

* [Firestore Redux Example](example/firestore_redux) - Uses the [Redux](https://pub.dartlang.org/packages/redux) library to manage app state and update Widgets and
adds [Cloud_Firestore](https://firebase.google.com/docs/firestore/) as the Todos database.

### Supporting Code

* [integration_tests](example/integration_tests) - Demonstrates how to write selenium-style integration (aka end to end) tests using the Page Object Model. This test suite is run against all samples.
Expand Down
11 changes: 11 additions & 0 deletions example/firestore_redux/.gitignore
@@ -0,0 +1,11 @@
.DS_Store
.atom/
.idea
.packages
.pub/
build/
ios/.generated/
packages
pubspec.lock
.flutter-plugins
Podfile.lock
123 changes: 123 additions & 0 deletions example/firestore_redux/README.md
@@ -0,0 +1,123 @@
# firestore redux sample




This repo started with [flutter_architecture_redux sample](https://github.com/brianegan/flutter_architecture_samples/blob/master/example/redux/README.md),
and added [Cloud_Firestore](https://firebase.google.com/docs/firestore/) as the backend database. Cloud Firestore
provides realtime connection between the database and authenticated devices, as well as automatic offline
persistence for Android and iOS. Firebase authentication is included for anonymous authentication of users.

# Set-up

The steps below were developed from [MemeChat repo](https://github.com/efortuna/memechat/blob/master/README.md).
There is a very useful [video tutorial](https://www.youtube.com/watch?v=w2TcYP8qiRI) associated with the MemeChat
repo from 2017 Google I/O that covers some basics related to connecting to Firebase.
In the present case, Firestore is being used but set up is similar.

1) Set up a Firestore instance at [Firebase Console](https://console.firebase.google.com/).

2) Enable anonymous authentication by going to 'Authentication' in left hand menu, selecting
'Sign-in Method', and enabling Anonymous at the bottom of the page.

3) For Android:

- Create an app within your Firebase instance for Android, with package name com.yourcompany.fireredux.
- Follow instructions to download google-services.json, and place it into fire_redux/android/app/.
- Set the defaultConfig.applicationID in `android/app/build.gradle` to match
android_client_info.package_name in `google-services.json`, e.g. `com.yourcompany.firereduxandroid`.
This is the name of your Android app in Firebase.
Package names must match between files `android/app/src/main/AndroidManifest.xml` and
`android/app/src/main/java/yourcompany/redux/MainActivity.java`, e.g. `com.yourcompany.fireredux`.
- Run the following command to get your SHA-1 key:

`keytool -exportcert -list -v \
-alias androiddebugkey -keystore ~/.android/debug.keystore`
- In the Firebase console, in the settings of your Android app, add your SHA-1 key by clicking "Add Fingerprint".
- To connect to Firestore be sure your project is using Gradle 4.1 and Android Studio Gradle plugin 3.0.1.
If not, then follow these
[upgrades steps](https://github.com/flutter/flutter/wiki/Updating-Flutter-projects-to-Gradle-4.1-and-Android-Studio-Gradle-plugin-3.0.1).
You will need to edit these files: `android/gradle/wrapper/gradle-wrapper.properties`,
`android/build.gradle`, and `android/app/build.gradle`.

4) For iOS:

- Create an app within your Firebase instance for iOS, with package name com.yourcompany.fireredux.
- Follow instructions to download GoogleService-Info.plist, and place it into fire_redux/ios/Runner.
- Open fire_redux/ios/Runner/Info.plist. Locate the CFBundleURLSchemes key.
The second item in the array value of this key is specific to the Firebase instance.
Replace it with the value for REVERSED_CLIENT_ID from GoogleService-Info.plist. It will look like this:
```$xslt
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>com.yourcompany.firereduxios</string>
<string>com.googleusercontent.apps.631911544122-jtjdk7lmrqoiup15hofsceegpfn0dhj6</string>
</array>
</dict>
</array>
```
- To successfully run on iOS, it may be necessary to manually copy GoogleService-Info.plist
to your Xcode project. After you attempt a run/build of your project on iOS open Xcode by
clicking on fire_redux/ios/Runner.xcworkspace. When your project is open in Xcode, then copy
GoogleService-Info.plist to Runner/Runner folder. Then your project should run on iOS.


# Summary of changes made to the original redux sample repo.

a) Added `firebase_auth` and `cloud_firestore` to pubspec.yaml.

b) Created new file `firestore_services.dart`. Class `FirestoreServices` in this file
provides complete interface to Firestore and is called only from `main.dart`.
This file acts as the data layer for the app and updates the redux store automatically
whenever there is a change to the Firestore todos data. The app UI is connected to the redux
store per the original redux sample repo.
Changes were made to actions, reducers, and middleware files from original repo
based on this new data source.

c) Only one widget was modified from the original repo. This was in `extra_actions_container.dart`.
```apple js
store.dispatch(new ToggleAllAction(
allCompleteSelector(todosSelector(store.state))));
```
Here an argument was added to ToggleAllAction to ensure all Todos are toggled correctly to
complete or active. Otherwise issues arose due to the propagation times from the app to
Firestore and back again.

For integration testing, the following files were created to avoid using Firestore and instead
access the original repo's local data source: `package:todos_repository/src/web_client.dart`.

`test/firestore_services_mock.dart` - the mocked data service.

`test/main_fire_4test.dart` - this is a copy of main.dart and replaces firestore_services with
firestore_services_mock. Any changes made to a new project main.dart should be replicated here
for testing purposes.

Additionally, `test_driver/todo_app.dart` was modified to import `test/main_fire_4test.dart`
rather than main.dart.



1) `flutter test` will run all unit tests.

a) `flutter test test/selectors_test.dart` for selectors unit testing.

b) `flutter test test/reducer_test.dart` for reducers unit testing.

c) `flutter test test/middleware_test.dart` for middleware unit testing.

2) `flutter drive --target=test_driver/todo_app.dart` to run integrations test.
Integrations tests are unchanged from the original redux repo.


Please see original repo
[flutter_architecture_redux sample](https://github.com/brianegan/flutter_architecture_samples/blob/master/example/redux/README.md)
for all details related to [redux](https://pub.dartlang.org/packages/redux)
and [flutter_redux](https://pub.dartlang.org/packages/flutter_redux).

Special thanks to [brianegan](https://github.com/brianegan) for providing such a wonderful repo:
[Flutter architecture samples](https://github.com/brianegan/flutter_architecture_samples/blob/master/README.md)!
12 changes: 12 additions & 0 deletions example/firestore_redux/android.iml
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/android">
<sourceFolder url="file://$MODULE_DIR$/android/app/src/main/java" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Flutter for Android" level="project" />
</component>
</module>
9 changes: 9 additions & 0 deletions example/firestore_redux/android/.gitignore
@@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
GeneratedPluginRegistrant.java
57 changes: 57 additions & 0 deletions example/firestore_redux/android/app/build.gradle
@@ -0,0 +1,57 @@
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 26
buildToolsVersion '26.0.3'

lintOptions {
disable 'InvalidPackage'
}

defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.yourcompany.firereduxandroid"
minSdkVersion 16
targetSdkVersion 26
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
}
profile {
matchingFallbacks = ['debug', 'release']
}
}
}

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'
}

apply plugin: 'com.google.gms.google-services'
39 changes: 39 additions & 0 deletions example/firestore_redux/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,39 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yourcompany.fireredux">

<!-- 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="fire redux"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
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>
@@ -0,0 +1,14 @@
package com.yourcompany.fireredux;

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);
}
}
@@ -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.
@@ -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>
28 changes: 28 additions & 0 deletions example/firestore_redux/android/build.gradle
@@ -0,0 +1,28 @@
buildscript {
repositories {
google()
jcenter()
}

dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'com.google.gms:google-services:3.1.0'
}
}

allprojects {
repositories {
google()
jcenter()
}
}

rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
delete rootProject.buildDir
}
1 change: 1 addition & 0 deletions example/firestore_redux/android/gradle.properties
@@ -0,0 +1 @@
org.gradle.jvmargs=-Xmx1536M
Binary file not shown.
@@ -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.1-all.zip

0 comments on commit f4dd437

Please sign in to comment.