diff --git a/platform_channels/android/app/src/main/assets/eat_new_orleans.jpg b/platform_channels/android/app/src/main/assets/eat_new_orleans.jpg new file mode 100644 index 000000000..517759ee1 Binary files /dev/null and b/platform_channels/android/app/src/main/assets/eat_new_orleans.jpg differ diff --git a/platform_channels/android/app/src/main/kotlin/dev/flutter/platform_channels/MainActivity.kt b/platform_channels/android/app/src/main/kotlin/dev/flutter/platform_channels/MainActivity.kt index 537ba1146..dd302c820 100644 --- a/platform_channels/android/app/src/main/kotlin/dev/flutter/platform_channels/MainActivity.kt +++ b/platform_channels/android/app/src/main/kotlin/dev/flutter/platform_channels/MainActivity.kt @@ -9,8 +9,11 @@ import android.hardware.Sensor import android.hardware.SensorManager import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.BasicMessageChannel import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.StandardMessageCodec +import java.io.InputStream class MainActivity : FlutterActivity() { override fun configureFlutterEngine(flutterEngine: FlutterEngine) { @@ -40,5 +43,15 @@ class MainActivity : FlutterActivity() { val accelerometerSensor: Sensor = sensorManger.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) EventChannel(flutterEngine.dartExecutor, "eventChannelDemo") .setStreamHandler(AccelerometerStreamHandler(sensorManger, accelerometerSensor)) + + // Registers a MessageHandler for BasicMessageChannel to receive a message from Dart and send + // image data in reply. + BasicMessageChannel(flutterEngine.dartExecutor, "platformImageDemo", StandardMessageCodec()) + .setMessageHandler { message, reply -> + if (message == "getImage") { + val inputStream: InputStream = assets.open("eat_new_orleans.jpg") + reply.reply(inputStream.readBytes()); + } + } } } diff --git a/platform_channels/assets/eat_new_orleans.jpg b/platform_channels/assets/eat_new_orleans.jpg new file mode 100644 index 000000000..517759ee1 Binary files /dev/null and b/platform_channels/assets/eat_new_orleans.jpg differ diff --git a/platform_channels/lib/main.dart b/platform_channels/lib/main.dart index fdef8d478..ffc0b31de 100644 --- a/platform_channels/lib/main.dart +++ b/platform_channels/lib/main.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:platform_channels/src/event_channel_demo.dart'; import 'package:platform_channels/src/method_channel_demo.dart'; +import 'package:platform_channels/src/platform_image_demo.dart'; void main() { runApp(PlatformChannelSample()); @@ -17,6 +18,7 @@ class PlatformChannelSample extends StatelessWidget { routes: { '/methodChannelDemo': (context) => MethodChannelDemo(), '/eventChannelDemo': (context) => EventChannelDemo(), + '/platformImageDemo': (context) => PlatformImageDemo(), }, title: 'Platform Channel Sample', home: HomePage(), @@ -41,6 +43,10 @@ List demoList = [ DemoInfo( 'EventChannel Demo', '/eventChannelDemo', + ), + DemoInfo( + 'Platform Image Demo', + '/platformImageDemo', ) ]; diff --git a/platform_channels/lib/src/image_basic_message_channel.dart b/platform_channels/lib/src/image_basic_message_channel.dart new file mode 100644 index 000000000..d3cfa82e1 --- /dev/null +++ b/platform_channels/lib/src/image_basic_message_channel.dart @@ -0,0 +1,28 @@ +// Copyright 2020 The Flutter team. 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:typed_data'; + +import 'package:flutter/services.dart'; + +/// This class manages a [BasicMessageChannel] that can return an image loaded +/// from a native asset. The [BasicMessageChannel] uses [StandardMessageCodec] +/// since it supports [Uint8List], which is used to transport the image data. +class PlatformImageFetcher { + static final _basicMessageChannel = const BasicMessageChannel( + 'platformImageDemo', StandardMessageCodec()); + + /// Method responsible for providing the platform image. + static Future getImage() async { + final reply = await _basicMessageChannel.send('getImage') as Uint8List; + if (reply == null) { + throw PlatformException( + code: 'Error', + message: 'Failed to load Platform Image', + details: null, + ); + } + return reply; + } +} diff --git a/platform_channels/lib/src/platform_image_demo.dart b/platform_channels/lib/src/platform_image_demo.dart new file mode 100644 index 000000000..c80d6672d --- /dev/null +++ b/platform_channels/lib/src/platform_image_demo.dart @@ -0,0 +1,76 @@ +// Copyright 2020 The Flutter team. 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:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:platform_channels/src/image_basic_message_channel.dart'; + +/// Demonstrates how to use [BasicMessageChannel] to get an image from platform. +/// +/// The widget uses [Image.memory] to display the image obtained from the +/// platform. +class PlatformImageDemo extends StatefulWidget { + @override + _PlatformImageDemoState createState() => _PlatformImageDemoState(); +} + +class _PlatformImageDemoState extends State { + Future imageData; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Platform Image Demo'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + child: FractionallySizedBox( + widthFactor: 1, + heightFactor: 0.6, + child: FutureBuilder( + future: imageData, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.none) { + return Placeholder(); + } else if (snapshot.hasError) { + return Center( + child: Text(snapshot.error.toString()), + ); + } else if (snapshot.connectionState == + ConnectionState.done) { + return Image.memory( + snapshot.data, + fit: BoxFit.fill, + ); + } + return CircularProgressIndicator(); + }, + ), + ), + ), + SizedBox( + height: 16, + ), + RaisedButton( + onPressed: imageData != null + ? null + : () { + setState(() { + imageData = PlatformImageFetcher.getImage(); + }); + }, + child: Text('Get Image'), + ) + ], + ), + ), + ); + } +} diff --git a/platform_channels/pubspec.yaml b/platform_channels/pubspec.yaml index c755c4035..b1f4b917f 100644 --- a/platform_channels/pubspec.yaml +++ b/platform_channels/pubspec.yaml @@ -20,3 +20,5 @@ dev_dependencies: flutter: uses-material-design: true + assets: + - assets/ diff --git a/platform_channels/test/src/platform_image_demo_test.dart b/platform_channels/test/src/platform_image_demo_test.dart new file mode 100644 index 000000000..d2d8dbd46 --- /dev/null +++ b/platform_channels/test/src/platform_image_demo_test.dart @@ -0,0 +1,38 @@ +// Copyright 2020 The Flutter team. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:platform_channels/src/platform_image_demo.dart'; + +void main() { + group('Platform Image Demo tests', () { + setUpAll(() { + // Register a mock for MessageHandler. + BasicMessageChannel('platformImageDemo', StandardMessageCodec()) + .setMockMessageHandler((dynamic message) async { + var byteData = await rootBundle.load('assets/eat_new_orleans.jpg'); + return byteData.buffer.asUint8List(); + }); + }); + + testWidgets('Platform Image test', (tester) async { + await tester.pumpWidget(MaterialApp( + home: PlatformImageDemo(), + )); + + // Initially a PlaceHolder is displayed when imageData is null. + expect(find.byType(Placeholder), findsOneWidget); + expect(find.byType(Image), findsNothing); + + // Tap on RaisedButton to get Image. + await tester.tap(find.byType(RaisedButton)); + await tester.pumpAndSettle(); + + expect(find.byType(Placeholder), findsNothing); + expect(find.byType(Image), findsOneWidget); + }); + }); +}