diff --git a/examples/platform_view/.gitignore b/examples/platform_view/.gitignore new file mode 100644 index 00000000000..eb15c3d27ca --- /dev/null +++ b/examples/platform_view/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +.atom/ +.idea +.packages +.pub/ +build/ +ios/.generated/ +packages +pubspec.lock +.flutter-plugins diff --git a/examples/platform_view/README.md b/examples/platform_view/README.md new file mode 100644 index 00000000000..c677d67f8d7 --- /dev/null +++ b/examples/platform_view/README.md @@ -0,0 +1,3 @@ +# Example of switching between full-screen Flutter and Platform View + +This project demonstrates how to bring up a full-screen iOS/Android view from a full-screen Flutter view along with passing data back and forth between the two. \ No newline at end of file diff --git a/examples/platform_view/android.iml b/examples/platform_view/android.iml new file mode 100644 index 00000000000..462b903e05b --- /dev/null +++ b/examples/platform_view/android.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/examples/platform_view/android/.gitignore b/examples/platform_view/android/.gitignore new file mode 100644 index 00000000000..1fd9325cac4 --- /dev/null +++ b/examples/platform_view/android/.gitignore @@ -0,0 +1,13 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +GeneratedPluginRegistrant.java + +/gradle +/gradlew +/gradlew.bat diff --git a/examples/platform_view/android/app/build.gradle b/examples/platform_view/android/app/build.gradle new file mode 100644 index 00000000000..dbab1622467 --- /dev/null +++ b/examples/platform_view/android/app/build.gradle @@ -0,0 +1,51 @@ +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 25 + buildToolsVersion '25.0.3' + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "io.flutter.examples.platform_view" + } + + 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 { + androidTestCompile 'com.android.support:support-annotations:25.0.0' + androidTestCompile 'com.android.support.test:runner:0.5' + androidTestCompile 'com.android.support.test:rules:0.5' + compile 'com.android.support:appcompat-v7:25.0.0' + compile 'com.android.support:design:25.0.0' +} diff --git a/examples/platform_view/android/app/src/main/AndroidManifest.xml b/examples/platform_view/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..770618bf28a --- /dev/null +++ b/examples/platform_view/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + diff --git a/examples/platform_view/android/app/src/main/java/io/flutter/examples/platform_view/CountActivity.java b/examples/platform_view/android/app/src/main/java/io/flutter/examples/platform_view/CountActivity.java new file mode 100644 index 00000000000..2feca7721d1 --- /dev/null +++ b/examples/platform_view/android/app/src/main/java/io/flutter/examples/platform_view/CountActivity.java @@ -0,0 +1,66 @@ +// Copyright 2017, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package io.flutter.examples.platform_view; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +public class CountActivity extends AppCompatActivity { + public static final String EXTRA_COUNTER = "counter"; + private int counter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.android_full_screen_layout); + Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar); + myToolbar.setTitle("Platform View"); + setSupportActionBar(myToolbar); + + counter = getIntent().getIntExtra(EXTRA_COUNTER, 0); + updateText(); + + FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); + fab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + counter++; + updateText(); + } + }); + + Button switchViewButton = (Button) findViewById(R.id.button); + switchViewButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + returnToFlutterView(); + } + }); + } + + private void updateText() { + TextView textView = (TextView) findViewById(R.id.button_tap); + String value = "Button tapped " + counter + (counter == 1 ? " time" : " times"); + textView.setText(value); + } + + private void returnToFlutterView() { + Intent returnIntent = new Intent(); + returnIntent.putExtra(EXTRA_COUNTER, counter); + setResult(Activity.RESULT_OK, returnIntent); + finish(); + } + + public void onBackPressed() { + returnToFlutterView(); + } +} diff --git a/examples/platform_view/android/app/src/main/java/io/flutter/examples/platform_view/MainActivity.java b/examples/platform_view/android/app/src/main/java/io/flutter/examples/platform_view/MainActivity.java new file mode 100644 index 00000000000..a701cc86b7c --- /dev/null +++ b/examples/platform_view/android/app/src/main/java/io/flutter/examples/platform_view/MainActivity.java @@ -0,0 +1,60 @@ +// Copyright 2017, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package io.flutter.examples.platform_view; + +import android.content.Intent; +import android.os.Bundle; + +import io.flutter.plugins.GeneratedPluginRegistrant; + +import io.flutter.app.FlutterActivity; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; + +public class MainActivity extends FlutterActivity { + private static final String CHANNEL = "samples.flutter.io/platform_view"; + private static final String METHOD_SWITCH_VIEW = "switchView"; + private static final int COUNT_REQUEST = 1; + + private MethodChannel.Result result; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + GeneratedPluginRegistrant.registerWith(this); + + new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler( + new MethodChannel.MethodCallHandler() { + @Override + public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) { + MainActivity.this.result = result; + int count = methodCall.arguments(); + if (methodCall.method.equals(METHOD_SWITCH_VIEW)) { + onlaunchFullScreen(count); + } else { + result.notImplemented(); + } + } + } + ); + } + + private void onlaunchFullScreen(int count) { + Intent fullScreenIntent = new Intent(this, CountActivity.class); + fullScreenIntent.putExtra(CountActivity.EXTRA_COUNTER, count); + startActivityForResult(fullScreenIntent, COUNT_REQUEST); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == COUNT_REQUEST) { + if (resultCode == RESULT_OK) { + result.success(data.getIntExtra(CountActivity.EXTRA_COUNTER, 0)); + } else { + result.error("ACTIVITY_FAILURE", "Failed while launching activity", null); + } + } + } +} diff --git a/examples/platform_view/android/app/src/main/res/drawable/ic_add_black_24dp.xml b/examples/platform_view/android/app/src/main/res/drawable/ic_add_black_24dp.xml new file mode 100644 index 00000000000..0258249cc48 --- /dev/null +++ b/examples/platform_view/android/app/src/main/res/drawable/ic_add_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/examples/platform_view/android/app/src/main/res/layout/android_full_screen_layout.xml b/examples/platform_view/android/app/src/main/res/layout/android_full_screen_layout.xml new file mode 100644 index 00000000000..370d4136f19 --- /dev/null +++ b/examples/platform_view/android/app/src/main/res/layout/android_full_screen_layout.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/platform_view/ios/Runner/GeneratedPluginRegistrant.h b/examples/platform_view/ios/Runner/GeneratedPluginRegistrant.h new file mode 100644 index 00000000000..3b700eb4819 --- /dev/null +++ b/examples/platform_view/ios/Runner/GeneratedPluginRegistrant.h @@ -0,0 +1,14 @@ +// +// Generated file. Do not edit. +// + +#ifndef GeneratedPluginRegistrant_h +#define GeneratedPluginRegistrant_h + +#import + +@interface GeneratedPluginRegistrant : NSObject ++ (void)registerWithRegistry:(NSObject*)registry; +@end + +#endif /* GeneratedPluginRegistrant_h */ diff --git a/examples/platform_view/ios/Runner/GeneratedPluginRegistrant.m b/examples/platform_view/ios/Runner/GeneratedPluginRegistrant.m new file mode 100644 index 00000000000..60dfa42b328 --- /dev/null +++ b/examples/platform_view/ios/Runner/GeneratedPluginRegistrant.m @@ -0,0 +1,12 @@ +// +// Generated file. Do not edit. +// + +#import "GeneratedPluginRegistrant.h" + +@implementation GeneratedPluginRegistrant + ++ (void)registerWithRegistry:(NSObject*)registry { +} + +@end diff --git a/examples/platform_view/ios/Runner/Info.plist b/examples/platform_view/ios/Runner/Info.plist new file mode 100644 index 00000000000..e23e76d59cf --- /dev/null +++ b/examples/platform_view/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + platform_view + 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/examples/platform_view/ios/Runner/PlatformViewController.h b/examples/platform_view/ios/Runner/PlatformViewController.h new file mode 100644 index 00000000000..26f501d18c9 --- /dev/null +++ b/examples/platform_view/ios/Runner/PlatformViewController.h @@ -0,0 +1,15 @@ +// Copyright 2017, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#import + +@protocol PlatformViewControllerDelegate +- (void)didUpdateCounter:(int)counter; +@end + + +@interface PlatformViewController : UIViewController +@property (strong, nonatomic) id delegate; +@property int counter; +@end diff --git a/examples/platform_view/ios/Runner/PlatformViewController.m b/examples/platform_view/ios/Runner/PlatformViewController.m new file mode 100644 index 00000000000..53de939dfb8 --- /dev/null +++ b/examples/platform_view/ios/Runner/PlatformViewController.m @@ -0,0 +1,39 @@ +// Copyright 2017, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#import +#import "PlatformViewController.h" + +@interface PlatformViewController () +@property (weak, nonatomic) IBOutlet UILabel *incrementLabel; +@end + +@implementation PlatformViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + [self setIncrementLabelText]; +} + +- (IBAction)handleIncrement:(id)sender { + self.counter++; + [self setIncrementLabelText]; +} + +- (IBAction)switchToFlutterView:(id)sender { + [self.delegate didUpdateCounter:self.counter]; + [self dismissViewControllerAnimated:NO completion:nil]; +} + +- (void)setIncrementLabelText { + NSString* text = [NSString stringWithFormat:@"Button tapped %d %@.", + self.counter, + (self.counter == 1) ? @"time" : @"times"]; + self.incrementLabel.text = text; +} + +@end + + + diff --git a/examples/platform_view/ios/Runner/ic_add.png b/examples/platform_view/ios/Runner/ic_add.png new file mode 100644 index 00000000000..23bf119211e Binary files /dev/null and b/examples/platform_view/ios/Runner/ic_add.png differ diff --git a/examples/platform_view/ios/Runner/main.m b/examples/platform_view/ios/Runner/main.m new file mode 100644 index 00000000000..1bdb8e28d1d --- /dev/null +++ b/examples/platform_view/ios/Runner/main.m @@ -0,0 +1,10 @@ +#import +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, + NSStringFromClass([AppDelegate class])); + } +} diff --git a/examples/platform_view/lib/main.dart b/examples/platform_view/lib/main.dart new file mode 100644 index 00000000000..b6426b5eff3 --- /dev/null +++ b/examples/platform_view/lib/main.dart @@ -0,0 +1,106 @@ +// Copyright 2017, the Flutter project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:async'; +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +void main() { + runApp(new PlatformView()); +} + +class PlatformView extends StatelessWidget { + @override + Widget build(BuildContext context) { + return new MaterialApp( + title: 'Platform View', + theme: new ThemeData( + primarySwatch: Colors.grey, + ), + home: new MyHomePage(title: 'Platform View'), + ); + } +} + +class MyHomePage extends StatefulWidget { + MyHomePage({Key key, this.title}) : super(key: key); + + final String title; + + @override + _MyHomePageState createState() => new _MyHomePageState(); +} + +class _MyHomePageState extends State { + static const MethodChannel _methodChannel = + const MethodChannel("samples.flutter.io/platform_view"); + + int _counter = 0; + + void _incrementCounter() { + setState(() { + _counter++; + }); + } + + Future _launchPlatformCount() async { + final int platformCounter = + await _methodChannel.invokeMethod("switchView", _counter); + setState(() { + _counter = platformCounter; + }); + } + + @override + Widget build(BuildContext context) => new Scaffold( + appBar: new AppBar( + title: new Text(widget.title), + ), + body: new Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + new Expanded( + child: new Center( + child: new Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + new Text( + 'Button tapped $_counter time${ _counter == 1 ? '' : 's' }.', + style: const TextStyle(fontSize: 17.0), + ), + new Padding( + padding: const EdgeInsets.all(18.0), + child: new RaisedButton( + child: Platform.isIOS + ? const Text('Continue in iOS view') + : const Text('Continue in Android view'), + onPressed: _launchPlatformCount), + ), + ], + ), + ), + ), + new Container( + padding: const EdgeInsets.only(bottom: 15.0, left: 5.0), + child: new Row( + children: [ + new Image.asset('assets/flutter-mark-square-64.png', + scale: 1.5), + const Text( + 'Flutter', + style: const TextStyle(fontSize: 30.0), + ), + ], + ), + ), + ], + ), + floatingActionButton: new FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: const Icon(Icons.add), + ), + ); +} diff --git a/examples/platform_view/pubspec.yaml b/examples/platform_view/pubspec.yaml new file mode 100644 index 00000000000..d5074882ac5 --- /dev/null +++ b/examples/platform_view/pubspec.yaml @@ -0,0 +1,11 @@ +name: platform_view + +dependencies: + flutter: + sdk: flutter + +flutter: + + uses-material-design: true + assets: + - assets/flutter-mark-square-64.png