Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transfer data from secondary to main display #38

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import android.view.ViewGroup
import android.widget.FrameLayout
import io.flutter.embedding.android.FlutterView
import io.flutter.embedding.engine.FlutterEngineCache
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel

class PresentationDisplay(context: Context, private val tag: String, display: Display) :

class PresentationDisplay(context: Context, private val tag: String, display: Display,private val callBack: (tip: Any?) -> Unit) :
Presentation(context, display) {

override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -30,6 +33,15 @@ class PresentationDisplay(context: Context, private val tag: String, display: Di
val flutterEngine = FlutterEngineCache.getInstance().get(tag)
if (flutterEngine != null) {
flutterView.attachToFlutterEngine(flutterEngine)

MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
"main_display_channel"
).setMethodCallHandler { call, result ->
if (call?.method == "transferDataToMain") {
callBack(call?.arguments);
}
}
} else {
Log.e("PresentationDisplay", "Can't find the FlutterEngine with cache name $tag")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class PresentationDisplaysPlugin : FlutterPlugin, ActivityAware, MethodChannel.M
private var flutterEngineChannel: MethodChannel? = null
private var context: Context? = null
private var presentation: PresentationDisplay? = null
private var flutterBinding: FlutterPlugin.FlutterPluginBinding? = null

override fun onAttachedToEngine(
@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding
Expand All @@ -43,6 +44,7 @@ class PresentationDisplaysPlugin : FlutterPlugin, ActivityAware, MethodChannel.M
DisplayManager
val displayConnectedStreamHandler = DisplayConnectedStreamHandler(displayManager)
eventChannel.setStreamHandler(displayConnectedStreamHandler)
flutterBinding = flutterPluginBinding
}

companion object {
Expand Down Expand Up @@ -72,6 +74,7 @@ class PresentationDisplaysPlugin : FlutterPlugin, ActivityAware, MethodChannel.M

override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
Log.i(TAG, "Channel: method: ${call.method} | arguments: ${call.arguments}")
val channelMainName: String = "main_display_channel"
when (call.method) {
"showPresentation" -> {
try {
Expand All @@ -86,11 +89,21 @@ class PresentationDisplaysPlugin : FlutterPlugin, ActivityAware, MethodChannel.M
val tag: String = obj.getString("routerName")
val display = displayManager?.getDisplay(displayId)
if (display != null) {
var dataToMainCallback: (Any?) -> Unit = {
argument : Any? ->
MethodChannel(
flutterBinding!!.flutterEngine.dartExecutor.binaryMessenger,
channelMainName
).invokeMethod(
"dataToMain", argument
)
}

val flutterEngine = createFlutterEngine(tag)
flutterEngine?.let {
flutterEngineChannel =
MethodChannel(it.dartExecutor.binaryMessenger, "${viewTypeId}_engine")
presentation = context?.let { it1 -> PresentationDisplay(it1, tag, display) }
presentation = context?.let { it1 -> PresentationDisplay(it1, tag, display, dataToMainCallback) }
Log.i(TAG, "presentation: $presentation")
presentation?.show()

Expand Down
107 changes: 89 additions & 18 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:presentation_displays/display.dart';
import 'package:presentation_displays/displays_manager.dart';
import 'package:presentation_displays/main_display.dart';
import 'package:presentation_displays/secondary_display.dart';

Route<dynamic> generateRoute(RouteSettings settings) {
Expand Down Expand Up @@ -96,6 +97,8 @@ class _DisplayManagerScreenState extends State<DisplayManagerScreen> {
final TextEditingController _nameOfIndexController = TextEditingController();
String _nameOfIndex = "";

dynamic dataFromMain;

@override
void initState() {
displayManager.connectedDisplaysChangedStream?.listen(
Expand All @@ -108,23 +111,31 @@ class _DisplayManagerScreenState extends State<DisplayManagerScreen> {

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
_getDisplays(),
_showPresentation(),
_hidePresentation(),
_transferData(),
_getDisplayeById(),
_getDisplayByIndex(),
],
return MainDisplay(
callback: (arguments) {
setState(() {
dataFromMain = arguments;
});
},
child: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
_getDisplays(),
_showPresentation(),
_hidePresentation(),
_transferData(),
_dataFromSecond(),
_getDisplayeById(),
_getDisplayByIndex(),
],
),
),
),
),
Expand Down Expand Up @@ -254,6 +265,20 @@ class _DisplayManagerScreenState extends State<DisplayManagerScreen> {
);
}

Widget _dataFromSecond() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text('Data from second: ${dataFromMain ?? '-'}'),
),
const Divider(),
],
);
}

Widget _getDisplayeById() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
Expand Down Expand Up @@ -336,6 +361,10 @@ class SecondaryScreen extends StatefulWidget {

class _SecondaryScreenState extends State<SecondaryScreen> {
String value = "init";
DisplayManager displayManager = DisplayManager();

final TextEditingController _dataToTransferController =
TextEditingController();

@override
Widget build(BuildContext context) {
Expand All @@ -349,9 +378,51 @@ class _SecondaryScreenState extends State<SecondaryScreen> {
child: Container(
color: Colors.white,
child: Center(
child: Text(value),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [_getTransferToMainButton(), _getDataFromMainText()],
),
),
),
));
}

Widget _getTransferToMainButton() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _dataToTransferController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Data',
),
),
),
Button(
title: "TransferDataToMain",
onPressed: () async {
displayManager.transferDataToMain(_dataToTransferController.text);
}),
const Divider(),
],
);
}

Widget _getDataFromMainText() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
height: 50,
child: Center(child: Text('Data from main: $value')),
),
const Divider(),
],
);
}
}
66 changes: 65 additions & 1 deletion lib/displays_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:presentation_displays/display.dart';
import 'package:presentation_displays/main_display.dart';
import 'package:presentation_displays/secondary_display.dart';

const _listDisplay = "listDisplay";
const _showPresentation = "showPresentation";
const _hidePresentation = "hidePresentation";
const _transferDataToPresentation = "transferDataToPresentation";
const _transferDataToMain = "transferDataToMain";

/// Display category: secondary display.
/// <p>
Expand All @@ -24,24 +26,28 @@ const _transferDataToPresentation = "transferDataToPresentation";
/// [DisplayManager.getDisplays], [DisplayManager.getNameByDisplayId],
/// [DisplayManager.getNameByIndex], [DisplayManager.showSecondaryDisplay],
/// [DisplayManager.transferDataToPresentation], [DisplayManager.hideSecondaryDisplay]
/// [DisplayManager.transferDataToMain]
/// </p>
///
/// [DisplayManager.getDisplays]
///
const String DISPLAY_CATEGORY_PRESENTATION =
"android.hardware.display.category.PRESENTATION";

/// Provide you with the method for you to work with [SecondaryDisplay].
/// Provide you with the method for you to work with [SecondaryDisplay] and [MainDisplay].
class DisplayManager {
final _displayMethodChannelId = "presentation_displays_plugin";
final _displayEventChannelId = "presentation_displays_plugin_events";
final _mainChannel = "main_display_channel";

late MethodChannel? _displayMethodChannel;
late EventChannel? _displayEventChannel;
MethodChannel? _mainMethodChannel;

DisplayManager() {
_displayMethodChannel = MethodChannel(_displayMethodChannelId);
_displayEventChannel = EventChannel(_displayEventChannelId);
_mainMethodChannel = MethodChannel(_mainChannel);
}

/// Gets all currently valid logical displays of the specified category.
Expand Down Expand Up @@ -202,4 +208,62 @@ class DisplayManager {
Stream<int?>? get connectedDisplaysChangedStream {
return _displayEventChannel?.receiveBroadcastStream().cast();
}

/// Transfer data to a main display
/// <p>
/// Transfer data from secondary screen to a main display
/// Consider using [arguments] for cases where a particular run-time type is expected. Consider using String when that run-time type is Map or JSONObject.
/// </p>
/// <p>
/// Main Screen
///
/// ```dart
/// class _MainScreenState extends State<MainScreen> {
/// @override
/// Widget build(BuildContext context) {
/// return MainDisplay(
/// callback: (argument) {
/// Song.fromJson(argument)
/// },
/// child: Center()
/// );
/// }
/// }
/// ```
///
/// ```
/// Secondary display
///
/// ```dart
/// DisplayManager displayManager = DisplayManager();
/// ...
/// static Future<void> transferData(Song song) async {
/// displayManager.transferDataToMain(<String, dynamic>{
/// 'id': song.id,
/// 'title': song.title,
/// 'artist': song.artist,
/// });
/// }
/// ```
/// Class Song
///
/// ```dart
/// class Song {
/// Song(this.id, this.title, this.artist);
///
/// final String id;
/// final String title;
/// final String artist;
///
/// static Song fromJson(dynamic json) {
/// return Song(json['id'], json['title'], json['artist']);
/// }
/// }
/// ```
/// </p>
///
/// return [Future<bool>] the value to determine whether or not the data has been transferred successfully
Future<bool?>? transferDataToMain(dynamic arguments) async {
return _mainMethodChannel?.invokeMethod(_transferDataToMain, arguments);
}
}
44 changes: 44 additions & 0 deletions lib/main_display.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:presentation_displays/displays_manager.dart';
import 'package:presentation_displays/secondary_display.dart';

/// This widget will wrap the main display, it will receive data transmitted from [DisplayManager].
/// [MainDisplay.callback] instance of [ArgumentsCallback] to receive data transmitted from the [DisplayManager].
/// [MainDisplay.child] child widget of main display
class MainDisplay extends StatefulWidget {
const MainDisplay({Key? key, required this.callback, required this.child})
: super(key: key);

/// instance of [ArgumentsCallback] to receive data transmitted from the [DisplaysManager].
final ArgumentsCallback callback;

/// Your Flutter UI on Main Screen
final Widget child;

@override
_MainDisplayState createState() => _MainDisplayState();
}

class _MainDisplayState extends State<MainDisplay> {
final _mainChannel = "main_display_channel";
MethodChannel? _mainMethodChannel;

@override
void initState() {
_addListenerForMain(widget.callback);
super.initState();
}

@override
Widget build(BuildContext context) {
return widget.child;
}

_addListenerForMain(ArgumentsCallback function) {
_mainMethodChannel = MethodChannel(_mainChannel);
_mainMethodChannel?.setMethodCallHandler((call) async {
function(call.arguments);
});
}
}