flutter: Support screen recording (Experimental)

This commit is contained in:
Calcitem 2021-05-29 01:11:56 +08:00
parent 2989407d31
commit 2b157caba7
5 changed files with 185 additions and 6 deletions

View File

@ -200,6 +200,30 @@
"@startNewGame": {
"description": "Start new game"
},
"startRecording": "Starte die Aufnahme",
"@startRecording": {
"description": "Start recording"
},
"recording": "Aufzeichnung...",
"@recording": {
"description": "Recording..."
},
"stopRecording": "Höre auf, aufzunehmen",
"@stopRecording": {
"description": "Stop recording"
},
"showRecording": "Aufnahme anzeigen",
"@showRecording": {
"description": "Show recording"
},
"noRecording": "Kein Aufnehmen.",
"@noRecording": {
"description": "No recording."
},
"pleaseWait": "Warten Sie mal...",
"@pleaseWait": {
"description": "Please wait..."
},
"restartGame": "Spiel neu beginnen?",
"@restartGame": {
"description": "Restart current Game?"
@ -935,5 +959,9 @@
"more": "Mehr",
"@more": {
"description": "More"
},
"experimental": "Dies ist eine experimentelle Funktion.",
"@experimental": {
"description": "Dies ist eine experimentelle Funktion."
}
}

View File

@ -200,6 +200,30 @@
"@startNewGame": {
"description": "Start new game"
},
"startRecording": "Start recording",
"@startRecording": {
"description": "Start recording"
},
"recording": "Recording...",
"@recording": {
"description": "Recording..."
},
"stopRecording": "Stop recording",
"@stopRecording": {
"description": "Stop recording"
},
"showRecording": "Show recording",
"@showRecording": {
"description": "Show recording"
},
"noRecording": "No recording.",
"@noRecording": {
"description": "No recording."
},
"pleaseWait": "Please wait...",
"@pleaseWait": {
"description": "Please wait..."
},
"restartGame": "Restart current Game?",
"@restartGame": {
"description": "Restart current Game?"
@ -935,5 +959,9 @@
"more": "More",
"@more": {
"description": "More"
},
"experimental": "This is an experimental feature.",
"@experimental": {
"description": "This is an experimental feature."
}
}

View File

@ -50,6 +50,12 @@
"thinking": "对方思考中...",
"newGame": "新局",
"startNewGame": "开始新局",
"startRecording": "开始录制",
"recording": "录制中...",
"stopRecording": "停止录制",
"showRecording": "回放录制",
"noRecording": "没有可回放的",
"pleaseWait": "请稍候...",
"restartGame": "重新开局?",
"restart": "重开局",
"gameStarted": "游戏开始 请落子",
@ -233,5 +239,6 @@
"atEnd": "已经到底了",
"tapBackAgainToLeave": "再次按返回键退出应用",
"environmentVariables": "环境变量",
"more": "更多"
"more": "更多",
"experimental": "此仍属实验性功能。"
}

View File

@ -17,6 +17,7 @@
*/
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -32,6 +33,7 @@ import 'package:sanmill/mill/types.dart';
import 'package:sanmill/services/audios.dart';
import 'package:sanmill/style/app_theme.dart';
import 'package:sanmill/widgets/game_settings_page.dart';
import 'package:screen_recorder/screen_recorder.dart';
import 'package:stack_trace/stack_trace.dart';
import 'board.dart';
@ -58,6 +60,10 @@ class _GamePageState extends State<GamePage> with RouteAware {
bool isReady = false;
bool isGoingToHistory = false;
late Timer timer;
ScreenRecorderController screenRecorderController = ScreenRecorderController(
pixelRatio: 1.0,
skipFramesBetweenCaptures: 0,
);
final String tag = "[game_page]";
@override
@ -479,6 +485,75 @@ class _GamePageState extends State<GamePage> with RouteAware {
}
}
onStartRecordingButtonPressed() async {
Navigator.of(context).pop();
showDialog(
context: context,
barrierDismissible: true,
builder: (context) => AlertDialog(
title: Text(
S.of(context).appName,
style: TextStyle(color: AppTheme.dialogTitleColor),
),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).experimental,
),
],
),
actions: <Widget>[
TextButton(
child: Text(S.of(context).ok),
onPressed: () => Navigator.of(context).pop(),
),
],
),
);
screenRecorderController.start();
showSnackBar(
S.of(context).recording,
duration: Duration(seconds: 1 << 31),
);
}
onStopRecordingButtonPressed() async {
Navigator.of(context).pop();
screenRecorderController.stop();
showSnackBar(
S.of(context).stopRecording,
duration: Duration(seconds: 2),
);
}
onShowRecordingButtonPressed() async {
Navigator.of(context).pop();
showSnackBar(
S.of(context).pleaseWait,
duration: Duration(seconds: 1 << 31),
);
var gif = await screenRecorderController.export();
ScaffoldMessenger.of(context).hideCurrentSnackBar();
if (gif == null) {
showSnackBar(S.of(context).noRecording);
return;
}
var image = Image.memory(
Uint8List.fromList(gif),
);
showDialog(
context: context,
builder: (context) {
return AlertDialog(backgroundColor: Colors.black, content: image);
},
);
}
onAutoReplayButtonPressed() async {
Navigator.of(context).pop();
@ -487,8 +562,9 @@ class _GamePageState extends State<GamePage> with RouteAware {
}
onGameButtonPressed() {
showDialog(
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
builder: (BuildContext context) {
return SimpleDialog(
backgroundColor: Colors.transparent,
@ -501,6 +577,33 @@ class _GamePageState extends State<GamePage> with RouteAware {
),
onPressed: onStartNewGameButtonPressed,
),
SizedBox(height: AppTheme.sizedBoxHeight),
SimpleDialogOption(
child: Text(
S.of(context).startRecording,
style: AppTheme.simpleDialogOptionTextStyle,
textAlign: TextAlign.center,
),
onPressed: onStartRecordingButtonPressed,
),
SizedBox(height: AppTheme.sizedBoxHeight),
SimpleDialogOption(
child: Text(
S.of(context).stopRecording,
style: AppTheme.simpleDialogOptionTextStyle,
textAlign: TextAlign.center,
),
onPressed: onStopRecordingButtonPressed,
),
SizedBox(height: AppTheme.sizedBoxHeight),
SimpleDialogOption(
child: Text(
S.of(context).showRecording,
style: AppTheme.simpleDialogOptionTextStyle,
textAlign: TextAlign.center,
),
onPressed: onShowRecordingButtonPressed,
),
/*
SizedBox(height: AppTheme.sizedBoxHeight),
SimpleDialogOption(
@ -1002,10 +1105,13 @@ class _GamePageState extends State<GamePage> with RouteAware {
);
}
void showSnackBar(String message) {
void showSnackBar(String message,
{Duration duration = const Duration(milliseconds: 4000)}) {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(message)));
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(message),
duration: duration,
));
}
String getInfoText() {
@ -1160,7 +1266,16 @@ class _GamePageState extends State<GamePage> with RouteAware {
return Scaffold(
backgroundColor: Color(Config.darkBackgroundColor),
body: Column(children: <Widget>[header, board, toolbar]),
body: Column(children: <Widget>[
header,
ScreenRecorder(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.width,
controller: screenRecorderController,
child: board,
),
toolbar
]),
);
}

View File

@ -25,6 +25,7 @@ dependencies:
device_info_plus_platform_interface: ^1.0.1
devicelocale: ^0.4.1
double_back_to_close_app: ^2.0.1
screen_recorder: ^0.0.2
dev_dependencies:
flutter_test: