Support FirebaseStorage with custom Firebase app (#524)

diff --git a/packages/firebase_storage/CHANGELOG.md b/packages/firebase_storage/CHANGELOG.md
index 379ebb4..f9a1739 100644
--- a/packages/firebase_storage/CHANGELOG.md
+++ b/packages/firebase_storage/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.3.3
+
+* Added support for initialization with a custom Firebase app.
+
 ## 0.3.2
 
 * Added support for StorageReference `writeToFile`.
diff --git a/packages/firebase_storage/android/src/main/java/io/flutter/plugins/firebase/storage/FirebaseStoragePlugin.java b/packages/firebase_storage/android/src/main/java/io/flutter/plugins/firebase/storage/FirebaseStoragePlugin.java
index f927bbf..46318d9 100755
--- a/packages/firebase_storage/android/src/main/java/io/flutter/plugins/firebase/storage/FirebaseStoragePlugin.java
+++ b/packages/firebase_storage/android/src/main/java/io/flutter/plugins/firebase/storage/FirebaseStoragePlugin.java
@@ -36,11 +36,22 @@
 
   private FirebaseStoragePlugin(Registrar registrar) {
     FirebaseApp.initializeApp(registrar.context());
-    this.firebaseStorage = FirebaseStorage.getInstance();
   }
 
   @Override
   public void onMethodCall(MethodCall call, final Result result) {
+    String app = call.argument("app");
+    String storageBucket = call.argument("bucket");
+    if (app == null && storageBucket == null) {
+      firebaseStorage = FirebaseStorage.getInstance();
+    } else if (storageBucket == null) {
+      firebaseStorage = FirebaseStorage.getInstance(FirebaseApp.getInstance(app));
+    } else if (app == null) {
+      firebaseStorage = FirebaseStorage.getInstance(storageBucket);
+    } else {
+      firebaseStorage = FirebaseStorage.getInstance(FirebaseApp.getInstance(app), storageBucket);
+    }
+
     switch (call.method) {
       case "StorageReference#putFile":
         putFile(call, result);
diff --git a/packages/firebase_storage/example/lib/main.dart b/packages/firebase_storage/example/lib/main.dart
index 2a6169e..6fcc752 100755
--- a/packages/firebase_storage/example/lib/main.dart
+++ b/packages/firebase_storage/example/lib/main.dart
@@ -7,24 +7,44 @@
 import 'dart:math';
 
 import 'package:flutter/material.dart';
+import 'package:firebase_core/firebase_core.dart';
 import 'package:firebase_storage/firebase_storage.dart';
 import 'package:http/http.dart' as http;
 
-void main() {
-  runApp(new MyApp());
+void main() async {
+  final FirebaseApp app = await FirebaseApp.configure(
+    name: 'test',
+    options: new FirebaseOptions(
+      googleAppID: Platform.isIOS
+          ? '1:159623150305:ios:4a213ef3dbd8997b'
+          : '1:159623150305:android:ef48439a0cc0263d',
+      gcmSenderID: '159623150305',
+      apiKey: 'AIzaSyChk3KEG7QYrs4kQPLP1tjJNxBTbfCAdgg',
+      projectID: 'flutter-firebase-plugins',
+    ),
+  );
+  final FirebaseStorage storage = new FirebaseStorage(
+      app: app, storageBucket: 'gs://flutter-firebase-plugins.appspot.com');
+  runApp(new MyApp(storage: storage));
 }
 
 class MyApp extends StatelessWidget {
+  MyApp({this.storage});
+  final FirebaseStorage storage;
+
   @override
   Widget build(BuildContext context) {
     return new MaterialApp(
       title: 'Flutter Storage Example',
-      home: new MyHomePage(),
+      home: new MyHomePage(storage: storage),
     );
   }
 }
 
 class MyHomePage extends StatefulWidget {
+  MyHomePage({this.storage});
+  final FirebaseStorage storage;
+
   @override
   _MyHomePageState createState() => new _MyHomePageState();
 }
@@ -45,7 +65,7 @@
     assert(await file.readAsString() == kTestString);
     final String rand = "${new Random().nextInt(10000)}";
     final StorageReference ref =
-        FirebaseStorage.instance.ref().child('text').child('foo$rand.txt');
+        widget.storage.ref().child('text').child('foo$rand.txt');
     final StorageUploadTask uploadTask =
         ref.putFile(file, const StorageMetadata(contentLanguage: "en"));
 
diff --git a/packages/firebase_storage/ios/Classes/FirebaseStoragePlugin.m b/packages/firebase_storage/ios/Classes/FirebaseStoragePlugin.m
index c00a089..1e20e59 100644
--- a/packages/firebase_storage/ios/Classes/FirebaseStoragePlugin.m
+++ b/packages/firebase_storage/ios/Classes/FirebaseStoragePlugin.m
@@ -19,6 +19,7 @@
 @end
 
 @implementation FLTFirebaseStoragePlugin {
+  FIRStorage *storage;
 }
 
 + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
@@ -40,6 +41,18 @@
 }
 
 - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
+  NSString *appName = call.arguments[@"app"];
+  NSString *storageBucket = call.arguments[@"bucket"];
+  if ([appName isEqual:[NSNull null]] && [storageBucket isEqual:[NSNull null]]) {
+    storage = [FIRStorage storage];
+  } else if ([appName isEqual:[NSNull null]]) {
+    storage = [FIRStorage storageWithURL:storageBucket];
+  } else if ([storageBucket isEqual:[NSNull null]]) {
+    storage = [FIRStorage storageForApp:[FIRApp appNamed:appName]];
+  } else {
+    storage = [FIRStorage storageForApp:[FIRApp appNamed:appName] URL:storageBucket];
+  }
+
   if ([@"StorageReference#putFile" isEqualToString:call.method]) {
     [self putFile:call result:result];
   } else if ([@"StorageReference#putData" isEqualToString:call.method]) {
@@ -84,7 +97,7 @@
   if (![metadataDictionary isEqual:[NSNull null]]) {
     metadata = [self buildMetadataFromDictionary:metadataDictionary];
   }
-  FIRStorageReference *fileRef = [[FIRStorage storage].reference child:path];
+  FIRStorageReference *fileRef = [storage.reference child:path];
   [fileRef putData:data
           metadata:metadata
         completion:^(FIRStorageMetadata *metadata, NSError *error) {
@@ -140,7 +153,7 @@
 - (void)getData:(FlutterMethodCall *)call result:(FlutterResult)result {
   NSNumber *maxSize = call.arguments[@"maxSize"];
   NSString *path = call.arguments[@"path"];
-  FIRStorageReference *ref = [[FIRStorage storage].reference child:path];
+  FIRStorageReference *ref = [storage.reference child:path];
   [ref dataWithMaxSize:[maxSize longLongValue]
             completion:^(NSData *_Nullable data, NSError *_Nullable error) {
               if (error != nil) {
@@ -162,7 +175,7 @@
   NSString *path = call.arguments[@"path"];
   NSString *filePath = call.arguments[@"filePath"];
   NSURL *localURL = [NSURL fileURLWithPath:filePath];
-  FIRStorageReference *ref = [[FIRStorage storage].reference child:path];
+  FIRStorageReference *ref = [storage.reference child:path];
   FIRStorageDownloadTask *task = [ref writeToFile:localURL];
   [task observeStatus:FIRStorageTaskStatusSuccess
               handler:^(FIRStorageTaskSnapshot *snapshot) {
@@ -178,7 +191,7 @@
 
 - (void)getMetadata:(FlutterMethodCall *)call result:(FlutterResult)result {
   NSString *path = call.arguments[@"path"];
-  FIRStorageReference *ref = [[FIRStorage storage].reference child:path];
+  FIRStorageReference *ref = [storage.reference child:path];
   [ref metadataWithCompletion:^(FIRStorageMetadata *metadata, NSError *error) {
     if (error != nil) {
       result(error.flutterError);
@@ -191,7 +204,7 @@
 - (void)updateMetadata:(FlutterMethodCall *)call result:(FlutterResult)result {
   NSString *path = call.arguments[@"path"];
   NSDictionary *metadataDictionary = call.arguments[@"metadata"];
-  FIRStorageReference *ref = [[FIRStorage storage].reference child:path];
+  FIRStorageReference *ref = [storage.reference child:path];
   [ref updateMetadata:[self buildMetadataFromDictionary:metadataDictionary]
            completion:^(FIRStorageMetadata *metadata, NSError *error) {
              if (error != nil) {
@@ -204,25 +217,25 @@
 
 - (void)getBucket:(FlutterMethodCall *)call result:(FlutterResult)result {
   NSString *path = call.arguments[@"path"];
-  FIRStorageReference *ref = [[FIRStorage storage].reference child:path];
+  FIRStorageReference *ref = [storage.reference child:path];
   result([ref bucket]);
 }
 
 - (void)getName:(FlutterMethodCall *)call result:(FlutterResult)result {
   NSString *path = call.arguments[@"path"];
-  FIRStorageReference *ref = [[FIRStorage storage].reference child:path];
+  FIRStorageReference *ref = [storage.reference child:path];
   result([ref name]);
 }
 
 - (void)getPath:(FlutterMethodCall *)call result:(FlutterResult)result {
   NSString *path = call.arguments[@"path"];
-  FIRStorageReference *ref = [[FIRStorage storage].reference child:path];
+  FIRStorageReference *ref = [storage.reference child:path];
   result([ref fullPath]);
 }
 
 - (void)getDownloadUrl:(FlutterMethodCall *)call result:(FlutterResult)result {
   NSString *path = call.arguments[@"path"];
-  FIRStorageReference *ref = [[FIRStorage storage].reference child:path];
+  FIRStorageReference *ref = [storage.reference child:path];
   [ref downloadURLWithCompletion:^(NSURL *URL, NSError *error) {
     if (error != nil) {
       result(error.flutterError);
@@ -234,7 +247,7 @@
 
 - (void) delete:(FlutterMethodCall *)call result:(FlutterResult)result {
   NSString *path = call.arguments[@"path"];
-  FIRStorageReference *ref = [[FIRStorage storage].reference child:path];
+  FIRStorageReference *ref = [storage.reference child:path];
   [ref deleteWithCompletion:^(NSError *error) {
     if (error != nil) {
       result(error.flutterError);
diff --git a/packages/firebase_storage/lib/firebase_storage.dart b/packages/firebase_storage/lib/firebase_storage.dart
index 8979ca9..82fca5f 100755
--- a/packages/firebase_storage/lib/firebase_storage.dart
+++ b/packages/firebase_storage/lib/firebase_storage.dart
@@ -7,16 +7,43 @@
 import 'dart:typed_data';
 
 import 'package:flutter/services.dart';
+import 'package:firebase_core/firebase_core.dart';
 
+/// FirebaseStorage is a service that supports uploading and downloading large
+/// objects to Google Cloud Storage.
 class FirebaseStorage {
   static const MethodChannel channel =
       const MethodChannel('plugins.flutter.io/firebase_storage');
 
-  static FirebaseStorage get instance => new FirebaseStorage();
+  /// Returns the [FirebaseStorage] instance, initialized with a custom
+  /// [FirebaseApp] if [app] is specified and a custom Google Cloud Storage
+  /// bucket if [storageBucket] is specified.
+  ///
+  /// The [storageBucket] argument is the gs:// url to the custom Firebase
+  /// Storage Bucket.
+  ///
+  /// The [app] argument is the custom [FirebaseApp].
+  FirebaseStorage({this.app, this.storageBucket});
 
-  StorageReference ref() {
-    return new StorageReference._(const <String>[], this);
-  }
+  static FirebaseStorage _instance = new FirebaseStorage();
+
+  /// The [FirebaseApp] instance to which this [FirebaseStorage] belongs.
+  ///
+  /// If null, the default [FirebaseApp] is used.
+  final FirebaseApp app;
+
+  /// The Google Cloud Storage bucket to which this [FirebaseStorage] belongs.
+  ///
+  /// If null, the storage bucket of the specified [FirebaseApp] is used.
+  final String storageBucket;
+
+  /// Returns the [FirebaseStorage] instance, initialized with the default
+  /// [FirebaseApp].
+  static FirebaseStorage get instance => _instance;
+
+  /// Creates a new [StorageReference] initialized at the root
+  /// Firebase Storage location.
+  StorageReference ref() => new StorageReference._(const <String>[], this);
 }
 
 class StorageReference {
@@ -73,8 +100,8 @@
   /// Asynchronously uploads a file to the currently specified
   /// [StorageReference], with an optional [metadata].
   StorageUploadTask putFile(File file, [StorageMetadata metadata]) {
-    final StorageFileUploadTask task =
-        new StorageFileUploadTask._(file, _pathComponents.join("/"), metadata);
+    final StorageFileUploadTask task = new StorageFileUploadTask._(
+        file, _firebaseStorage, _pathComponents.join("/"), metadata);
     task._start();
     return task;
   }
@@ -82,8 +109,8 @@
   /// Asynchronously uploads byte data to the currently specified
   /// [StorageReference], with an optional [metadata].
   StorageUploadTask putData(Uint8List data, [StorageMetadata metadata]) {
-    final StorageUploadTask task =
-        new StorageDataUploadTask._(data, _pathComponents.join("/"), metadata);
+    final StorageUploadTask task = new StorageDataUploadTask._(
+        data, _firebaseStorage, _pathComponents.join("/"), metadata);
     task._start();
     return task;
   }
@@ -92,6 +119,8 @@
   Future<String> getBucket() async {
     return await FirebaseStorage.channel
         .invokeMethod("StorageReference#getBucket", <String, String>{
+      'app': _firebaseStorage.app?.name,
+      'bucket': _firebaseStorage.storageBucket,
       'path': _pathComponents.join("/"),
     });
   }
@@ -101,6 +130,8 @@
   Future<String> getPath() async {
     return await FirebaseStorage.channel
         .invokeMethod("StorageReference#getPath", <String, String>{
+      'app': _firebaseStorage.app?.name,
+      'bucket': _firebaseStorage.storageBucket,
       'path': _pathComponents.join("/"),
     });
   }
@@ -109,6 +140,8 @@
   Future<String> getName() async {
     return await FirebaseStorage.channel
         .invokeMethod("StorageReference#getName", <String, String>{
+      'app': _firebaseStorage.app?.name,
+      'bucket': _firebaseStorage.storageBucket,
       'path': _pathComponents.join("/"),
     });
   }
@@ -119,6 +152,8 @@
     return await FirebaseStorage.channel.invokeMethod(
       "StorageReference#getData",
       <String, dynamic>{
+        'app': _firebaseStorage.app?.name,
+        'bucket': _firebaseStorage.storageBucket,
         'maxSize': maxSize,
         'path': _pathComponents.join("/"),
       },
@@ -128,8 +163,8 @@
   /// Asynchronously downloads the object at this [StorageReference] to a
   /// specified system file.
   StorageFileDownloadTask writeToFile(File file) {
-    final StorageFileDownloadTask task =
-        new StorageFileDownloadTask._(_pathComponents.join("/"), file);
+    final StorageFileDownloadTask task = new StorageFileDownloadTask._(
+        _firebaseStorage, _pathComponents.join("/"), file);
     task._start();
     return task;
   }
@@ -140,19 +175,27 @@
   Future<dynamic> getDownloadURL() async {
     return await FirebaseStorage.channel
         .invokeMethod("StorageReference#getDownloadUrl", <String, String>{
+      'app': _firebaseStorage.app?.name,
+      'bucket': _firebaseStorage.storageBucket,
       'path': _pathComponents.join("/"),
     });
   }
 
   Future<void> delete() {
-    return FirebaseStorage.channel.invokeMethod("StorageReference#delete",
-        <String, String>{'path': _pathComponents.join("/")});
+    return FirebaseStorage.channel
+        .invokeMethod("StorageReference#delete", <String, String>{
+      'app': _firebaseStorage.app?.name,
+      'bucket': _firebaseStorage.storageBucket,
+      'path': _pathComponents.join("/")
+    });
   }
 
   /// Retrieves metadata associated with an object at this [StorageReference].
   Future<StorageMetadata> getMetadata() async {
     return new StorageMetadata._fromMap(await FirebaseStorage.channel
         .invokeMethod("StorageReference#getMetadata", <String, String>{
+      'app': _firebaseStorage.app?.name,
+      'bucket': _firebaseStorage.storageBucket,
       'path': _pathComponents.join("/"),
     }));
   }
@@ -167,6 +210,8 @@
   Future<StorageMetadata> updateMetadata(StorageMetadata metadata) async {
     return new StorageMetadata._fromMap(await FirebaseStorage.channel
         .invokeMethod("StorageReference#updateMetadata", <String, dynamic>{
+      'app': _firebaseStorage.app?.name,
+      'bucket': _firebaseStorage.storageBucket,
       'path': _pathComponents.join("/"),
       'metadata': metadata == null ? null : _buildMetadataUploadMap(metadata),
     }));
@@ -255,15 +300,18 @@
 }
 
 class StorageFileDownloadTask {
+  final FirebaseStorage _firebaseStorage;
   final String _path;
   final File _file;
 
-  StorageFileDownloadTask._(this._path, this._file);
+  StorageFileDownloadTask._(this._firebaseStorage, this._path, this._file);
 
   Future<void> _start() async {
     final int totalByteCount = await FirebaseStorage.channel.invokeMethod(
       "StorageReference#writeToFile",
       <String, dynamic>{
+        'app': _firebaseStorage.app?.name,
+        'bucket': _firebaseStorage.storageBucket,
         'filePath': _file.absolute.path,
         'path': _path,
       },
@@ -278,10 +326,11 @@
 }
 
 abstract class StorageUploadTask {
+  final FirebaseStorage _firebaseStorage;
   final String _path;
   final StorageMetadata _metadata;
 
-  StorageUploadTask._(this._path, this._metadata);
+  StorageUploadTask._(this._firebaseStorage, this._path, this._metadata);
   Future<void> _start();
 
   Completer<UploadTaskSnapshot> _completer =
@@ -291,14 +340,17 @@
 
 class StorageFileUploadTask extends StorageUploadTask {
   final File _file;
-  StorageFileUploadTask._(this._file, String path, StorageMetadata metadata)
-      : super._(path, metadata);
+  StorageFileUploadTask._(this._file, FirebaseStorage firebaseStorage,
+      String path, StorageMetadata metadata)
+      : super._(firebaseStorage, path, metadata);
 
   @override
   Future<void> _start() async {
     final String downloadUrl = await FirebaseStorage.channel.invokeMethod(
       'StorageReference#putFile',
       <String, dynamic>{
+        'app': _firebaseStorage.app?.name,
+        'bucket': _firebaseStorage.storageBucket,
         'filename': _file.absolute.path,
         'path': _path,
         'metadata':
@@ -312,14 +364,17 @@
 
 class StorageDataUploadTask extends StorageUploadTask {
   final Uint8List _bytes;
-  StorageDataUploadTask._(this._bytes, String path, StorageMetadata metadata)
-      : super._(path, metadata);
+  StorageDataUploadTask._(this._bytes, FirebaseStorage firebaseStorage,
+      String path, StorageMetadata metadata)
+      : super._(firebaseStorage, path, metadata);
 
   @override
   Future<void> _start() async {
     final String downloadUrl = await FirebaseStorage.channel.invokeMethod(
       'StorageReference#putData',
       <String, dynamic>{
+        'app': _firebaseStorage.app?.name,
+        'bucket': _firebaseStorage.storageBucket,
         'data': _bytes,
         'path': _path,
         'metadata':
diff --git a/packages/firebase_storage/pubspec.yaml b/packages/firebase_storage/pubspec.yaml
index 17eb3fd..c168813 100755
--- a/packages/firebase_storage/pubspec.yaml
+++ b/packages/firebase_storage/pubspec.yaml
@@ -3,7 +3,7 @@
   cost-effective object storage service for Android and iOS.
 author: Flutter Team <flutter-dev@googlegroups.com>
 homepage: https://github.com/flutter/plugins/tree/master/packages/firebase_storage
-version: 0.3.2
+version: 0.3.3
 
 flutter:
   plugin:
@@ -14,6 +14,7 @@
 dependencies:
   flutter:
     sdk: flutter
+  firebase_core: ^0.2.3
 
 dev_dependencies:
   flutter_test:
diff --git a/packages/firebase_storage/test/firebase_storage_test.dart b/packages/firebase_storage/test/firebase_storage_test.dart
index ea23737..1697821 100644
--- a/packages/firebase_storage/test/firebase_storage_test.dart
+++ b/packages/firebase_storage/test/firebase_storage_test.dart
@@ -5,217 +5,296 @@
 import 'dart:async';
 import 'dart:typed_data';
 
+import 'package:firebase_core/firebase_core.dart';
 import 'package:firebase_storage/firebase_storage.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
 
 void main() {
-  group('StorageReference', () {
-    group('getData', () {
-      final List<MethodCall> log = <MethodCall>[];
+  group('FirebaseStorage', () {
+    final FirebaseApp app = const FirebaseApp(
+      name: 'testApp',
+    );
+    final String storageBucket = 'gs://fake-storage-bucket-url.com';
+    final FirebaseStorage storage =
+        new FirebaseStorage(app: app, storageBucket: storageBucket);
 
-      StorageReference ref;
+    group('StorageReference', () {
+      group('getData', () {
+        final List<MethodCall> log = <MethodCall>[];
 
-      setUp(() {
-        FirebaseStorage.channel
-            .setMockMethodCallHandler((MethodCall methodCall) {
-          log.add(methodCall);
-          return new Future<Uint8List>.value(
-              new Uint8List.fromList(<int>[1, 2, 3, 4]));
+        StorageReference ref;
+
+        setUp(() {
+          FirebaseStorage.channel
+              .setMockMethodCallHandler((MethodCall methodCall) {
+            log.add(methodCall);
+            return new Future<Uint8List>.value(
+                new Uint8List.fromList(<int>[1, 2, 3, 4]));
+          });
+          ref =
+              storage.ref().child('avatars').child('large').child('image.jpg');
         });
-        ref = FirebaseStorage.instance
-            .ref()
-            .child('avatars')
-            .child('large')
-            .child('image.jpg');
-      });
 
-      test('invokes correct method', () async {
-        await ref.getData(10);
+        test('invokes correct method', () async {
+          await ref.getData(10);
 
-        expect(log, <Matcher>[
-          isMethodCall(
-            'StorageReference#getData',
-            arguments: <String, dynamic>{
-              'maxSize': 10,
-              'path': 'avatars/large/image.jpg',
-            },
-          ),
-        ]);
-      });
-
-      test('returns correct result', () async {
-        expect(await ref.getData(10),
-            equals(new Uint8List.fromList(<int>[1, 2, 3, 4])));
-      });
-    });
-
-    group('getMetadata', () {
-      final List<MethodCall> log = <MethodCall>[];
-
-      StorageReference ref;
-
-      setUp(() {
-        FirebaseStorage.channel
-            .setMockMethodCallHandler((MethodCall methodCall) async {
-          log.add(methodCall);
-          return <String, String>{'name': 'image.jpg'};
-        });
-        ref = FirebaseStorage.instance
-            .ref()
-            .child('avatars')
-            .child('large')
-            .child('image.jpg');
-      });
-
-      test('invokes correct method', () async {
-        await ref.getMetadata();
-
-        expect(log, <Matcher>[
-          isMethodCall(
-            'StorageReference#getMetadata',
-            arguments: <String, dynamic>{
-              'path': 'avatars/large/image.jpg',
-            },
-          ),
-        ]);
-      });
-
-      test('returns correct result', () async {
-        expect((await ref.getMetadata()).name, 'image.jpg');
-      });
-    });
-
-    group('updateMetadata', () {
-      final List<MethodCall> log = <MethodCall>[];
-
-      StorageReference ref;
-
-      setUp(() {
-        FirebaseStorage.channel
-            .setMockMethodCallHandler((MethodCall methodCall) async {
-          log.add(methodCall);
-          switch (methodCall.method) {
-            case 'StorageReference#getMetadata':
-              return <String, String>{
-                'name': 'image.jpg',
-              };
-              break;
-            case 'StorageReference#updateMetadata':
-              return <String, String>{
-                'name': 'image.jpg',
-                'contentLanguage': 'en'
-              };
-              break;
-            default:
-              break;
-          }
-        });
-        ref = FirebaseStorage.instance
-            .ref()
-            .child('avatars')
-            .child('large')
-            .child('image.jpg');
-      });
-
-      test('invokes correct method', () async {
-        await ref.updateMetadata(const StorageMetadata(contentLanguage: 'en'));
-
-        expect(log, <Matcher>[
-          isMethodCall(
-            'StorageReference#updateMetadata',
-            arguments: <String, dynamic>{
-              'path': 'avatars/large/image.jpg',
-              'metadata': <String, String>{
-                'cacheControl': null,
-                'contentDisposition': null,
-                'contentLanguage': 'en',
-                'contentType': null,
-                'contentEncoding': null
-              },
-            },
-          ),
-        ]);
-      });
-
-      test('returns correct result', () async {
-        expect((await ref.getMetadata()).contentLanguage, null);
-        expect(
-            (await ref.updateMetadata(
-                    const StorageMetadata(contentLanguage: 'en')))
-                .contentLanguage,
-            'en');
-      });
-    });
-
-    group('getDownloadUrl', () {
-      final List<MethodCall> log = <MethodCall>[];
-
-      StorageReference ref;
-
-      setUp(() {
-        FirebaseStorage.channel
-            .setMockMethodCallHandler((MethodCall methodCall) async {
-          log.add(methodCall);
-          return 'https://path/to/file';
-        });
-        ref = FirebaseStorage.instance
-            .ref()
-            .child('avatars')
-            .child('large')
-            .child('image.jpg');
-      });
-
-      test('invokes correct method', () async {
-        await ref.getDownloadURL();
-
-        expect(log, <Matcher>[
-          isMethodCall(
-            'StorageReference#getDownloadUrl',
-            arguments: <String, dynamic>{
-              'path': 'avatars/large/image.jpg',
-            },
-          ),
-        ]);
-      });
-
-      test('returns correct result', () async {
-        expect(await ref.getDownloadURL(), 'https://path/to/file');
-      });
-    });
-
-    group('delete', () {
-      final List<MethodCall> log = <MethodCall>[];
-
-      StorageReference ref;
-
-      setUp(() {
-        FirebaseStorage.channel
-            .setMockMethodCallHandler((MethodCall methodCall) async {
-          log.add(methodCall);
-          return null;
-        });
-        ref = FirebaseStorage.instance.ref().child('image.jpg');
-      });
-
-      test('invokes correct method', () async {
-        await ref.delete();
-
-        expect(
-          log,
-          <Matcher>[
+          expect(log, <Matcher>[
             isMethodCall(
-              'StorageReference#delete',
+              'StorageReference#getData',
               arguments: <String, dynamic>{
-                'path': 'image.jpg',
+                'app': 'testApp',
+                'bucket': 'gs://fake-storage-bucket-url.com',
+                'maxSize': 10,
+                'path': 'avatars/large/image.jpg',
               },
             ),
-          ],
-        );
+          ]);
+        });
+
+        test('returns correct result', () async {
+          expect(await ref.getData(10),
+              equals(new Uint8List.fromList(<int>[1, 2, 3, 4])));
+        });
+      });
+
+      group('getMetadata', () {
+        final List<MethodCall> log = <MethodCall>[];
+
+        StorageReference ref;
+
+        setUp(() {
+          FirebaseStorage.channel
+              .setMockMethodCallHandler((MethodCall methodCall) async {
+            log.add(methodCall);
+            return <String, String>{'name': 'image.jpg'};
+          });
+          ref =
+              storage.ref().child('avatars').child('large').child('image.jpg');
+        });
+
+        test('invokes correct method', () async {
+          await ref.getMetadata();
+
+          expect(log, <Matcher>[
+            isMethodCall(
+              'StorageReference#getMetadata',
+              arguments: <String, dynamic>{
+                'app': 'testApp',
+                'bucket': 'gs://fake-storage-bucket-url.com',
+                'path': 'avatars/large/image.jpg',
+              },
+            ),
+          ]);
+        });
+
+        test('returns correct result', () async {
+          expect((await ref.getMetadata()).name, 'image.jpg');
+        });
+      });
+
+      group('updateMetadata', () {
+        final List<MethodCall> log = <MethodCall>[];
+
+        StorageReference ref;
+
+        setUp(() {
+          FirebaseStorage.channel
+              .setMockMethodCallHandler((MethodCall methodCall) async {
+            log.add(methodCall);
+            switch (methodCall.method) {
+              case 'StorageReference#getMetadata':
+                return <String, String>{
+                  'name': 'image.jpg',
+                };
+                break;
+              case 'StorageReference#updateMetadata':
+                return <String, String>{
+                  'name': 'image.jpg',
+                  'contentLanguage': 'en'
+                };
+                break;
+              default:
+                break;
+            }
+          });
+          ref =
+              storage.ref().child('avatars').child('large').child('image.jpg');
+        });
+
+        test('invokes correct method', () async {
+          await ref
+              .updateMetadata(const StorageMetadata(contentLanguage: 'en'));
+
+          expect(log, <Matcher>[
+            isMethodCall(
+              'StorageReference#updateMetadata',
+              arguments: <String, dynamic>{
+                'app': 'testApp',
+                'bucket': 'gs://fake-storage-bucket-url.com',
+                'path': 'avatars/large/image.jpg',
+                'metadata': <String, String>{
+                  'cacheControl': null,
+                  'contentDisposition': null,
+                  'contentLanguage': 'en',
+                  'contentType': null,
+                  'contentEncoding': null
+                },
+              },
+            ),
+          ]);
+        });
+
+        test('returns correct result', () async {
+          expect((await ref.getMetadata()).contentLanguage, null);
+          expect(
+              (await ref.updateMetadata(
+                      const StorageMetadata(contentLanguage: 'en')))
+                  .contentLanguage,
+              'en');
+        });
+      });
+
+      group('getDownloadUrl', () {
+        final List<MethodCall> log = <MethodCall>[];
+
+        StorageReference ref;
+
+        setUp(() {
+          FirebaseStorage.channel
+              .setMockMethodCallHandler((MethodCall methodCall) async {
+            log.add(methodCall);
+            return 'https://path/to/file';
+          });
+          ref =
+              storage.ref().child('avatars').child('large').child('image.jpg');
+        });
+
+        test('invokes correct method', () async {
+          await ref.getDownloadURL();
+
+          expect(log, <Matcher>[
+            isMethodCall(
+              'StorageReference#getDownloadUrl',
+              arguments: <String, dynamic>{
+                'app': 'testApp',
+                'bucket': 'gs://fake-storage-bucket-url.com',
+                'path': 'avatars/large/image.jpg',
+              },
+            ),
+          ]);
+        });
+
+        test('returns correct result', () async {
+          expect(await ref.getDownloadURL(), 'https://path/to/file');
+        });
+      });
+
+      group('delete', () {
+        final List<MethodCall> log = <MethodCall>[];
+
+        StorageReference ref;
+
+        setUp(() {
+          FirebaseStorage.channel
+              .setMockMethodCallHandler((MethodCall methodCall) async {
+            log.add(methodCall);
+            return null;
+          });
+          ref = storage.ref().child('image.jpg');
+        });
+
+        test('invokes correct method', () async {
+          await ref.delete();
+
+          expect(
+            log,
+            <Matcher>[
+              isMethodCall(
+                'StorageReference#delete',
+                arguments: <String, dynamic>{
+                  'app': 'testApp',
+                  'bucket': 'gs://fake-storage-bucket-url.com',
+                  'path': 'image.jpg',
+                },
+              ),
+            ],
+          );
+        });
+      });
+
+      group('getBucket', () {
+        final List<MethodCall> log = <MethodCall>[];
+
+        StorageReference ref;
+
+        setUp(() {
+          FirebaseStorage.channel
+              .setMockMethodCallHandler((MethodCall methodCall) async {
+            log.add(methodCall);
+            return 'foo';
+          });
+          ref =
+              storage.ref().child('avatars').child('large').child('image.jpg');
+        });
+
+        test('invokes correct method', () async {
+          await ref.getBucket();
+
+          expect(log, <Matcher>[
+            isMethodCall(
+              'StorageReference#getBucket',
+              arguments: <String, dynamic>{
+                'app': 'testApp',
+                'bucket': 'gs://fake-storage-bucket-url.com',
+                'path': 'avatars/large/image.jpg',
+              },
+            ),
+          ]);
+        });
+
+        test('returns correct result', () async {
+          expect(await ref.getBucket(), 'foo');
+        });
+      });
+
+      group('getName', () {
+        final List<MethodCall> log = <MethodCall>[];
+
+        StorageReference ref;
+
+        setUp(() {
+          FirebaseStorage.channel
+              .setMockMethodCallHandler((MethodCall methodCall) async {
+            log.add(methodCall);
+            return 'image.jpg';
+          });
+          ref =
+              storage.ref().child('avatars').child('large').child('image.jpg');
+        });
+
+        test('invokes correct method', () async {
+          await ref.getName();
+
+          expect(log, <Matcher>[
+            isMethodCall(
+              'StorageReference#getName',
+              arguments: <String, dynamic>{
+                'app': 'testApp',
+                'bucket': 'gs://fake-storage-bucket-url.com',
+                'path': 'avatars/large/image.jpg',
+              },
+            ),
+          ]);
+        });
+
+        test('returns correct result', () async {
+          expect(await ref.getName(), 'image.jpg');
+        });
       });
     });
 
-    group('getBucket', () {
+    group('getPath', () {
       final List<MethodCall> log = <MethodCall>[];
 
       StorageReference ref;
@@ -224,22 +303,20 @@
         FirebaseStorage.channel
             .setMockMethodCallHandler((MethodCall methodCall) async {
           log.add(methodCall);
-          return 'foo';
+          return 'avatars/large/image.jpg';
         });
-        ref = FirebaseStorage.instance
-            .ref()
-            .child('avatars')
-            .child('large')
-            .child('image.jpg');
+        ref = storage.ref().child('avatars').child('large').child('image.jpg');
       });
 
       test('invokes correct method', () async {
-        await ref.getBucket();
+        await ref.getPath();
 
         expect(log, <Matcher>[
           isMethodCall(
-            'StorageReference#getBucket',
+            'StorageReference#getPath',
             arguments: <String, dynamic>{
+              'app': 'testApp',
+              'bucket': 'gs://fake-storage-bucket-url.com',
               'path': 'avatars/large/image.jpg',
             },
           ),
@@ -247,80 +324,8 @@
       });
 
       test('returns correct result', () async {
-        expect(await ref.getBucket(), 'foo');
+        expect(await ref.getPath(), 'avatars/large/image.jpg');
       });
     });
-
-    group('getName', () {
-      final List<MethodCall> log = <MethodCall>[];
-
-      StorageReference ref;
-
-      setUp(() {
-        FirebaseStorage.channel
-            .setMockMethodCallHandler((MethodCall methodCall) async {
-          log.add(methodCall);
-          return 'image.jpg';
-        });
-        ref = FirebaseStorage.instance
-            .ref()
-            .child('avatars')
-            .child('large')
-            .child('image.jpg');
-      });
-
-      test('invokes correct method', () async {
-        await ref.getName();
-
-        expect(log, <Matcher>[
-          isMethodCall(
-            'StorageReference#getName',
-            arguments: <String, dynamic>{
-              'path': 'avatars/large/image.jpg',
-            },
-          ),
-        ]);
-      });
-
-      test('returns correct result', () async {
-        expect(await ref.getName(), 'image.jpg');
-      });
-    });
-  });
-
-  group('getPath', () {
-    final List<MethodCall> log = <MethodCall>[];
-
-    StorageReference ref;
-
-    setUp(() {
-      FirebaseStorage.channel
-          .setMockMethodCallHandler((MethodCall methodCall) async {
-        log.add(methodCall);
-        return 'avatars/large/image.jpg';
-      });
-      ref = FirebaseStorage.instance
-          .ref()
-          .child('avatars')
-          .child('large')
-          .child('image.jpg');
-    });
-
-    test('invokes correct method', () async {
-      await ref.getPath();
-
-      expect(log, <Matcher>[
-        isMethodCall(
-          'StorageReference#getPath',
-          arguments: <String, dynamic>{
-            'path': 'avatars/large/image.jpg',
-          },
-        ),
-      ]);
-    });
-
-    test('returns correct result', () async {
-      expect(await ref.getPath(), 'avatars/large/image.jpg');
-    });
   });
 }