Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
bcdab118
Unverified
Commit
bcdab118
authored
Mar 17, 2023
by
Greg Spencer
Committed by
GitHub
Mar 17, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add support for application exit requests (#121378)
Add support for application exit requests
parent
b1464e00
Changes
20
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
313 additions
and
25 deletions
+313
-25
Info.plist
dev/benchmarks/complex_layout/macos/Runner/Info.plist
+1
-1
Info.plist
dev/benchmarks/macrobenchmarks/macos/Runner/Info.plist
+1
-1
Info.plist
dev/integration_tests/channels/macos/Runner/Info.plist
+1
-1
Info.plist
dev/integration_tests/flavors/macos/Runner/Info.plist
+1
-1
Info.plist
...integration_tests/flutter_gallery/macos/Runner/Info.plist
+1
-1
Info.plist
dev/integration_tests/ui/macos/Runner/Info.plist
+1
-1
Info.plist
dev/manual_tests/macos/Runner/Info.plist
+1
-1
handle_request_app_exit.0.dart
...s/api/lib/services/binding/handle_request_app_exit.0.dart
+110
-0
Info.plist
examples/api/macos/Runner/Info.plist
+1
-1
handle_request_app_exit.0_test.dart
...test/services/binding/handle_request_app_exit.0_test.dart
+22
-0
Info.plist
examples/flutter_view/macos/Runner/Info.plist
+1
-1
Info.plist
examples/hello_world/macos/Runner/Info.plist
+1
-1
Info.plist
examples/image_list/macos/Runner/Info.plist
+1
-1
Info.plist
examples/layers/macos/Runner/Info.plist
+1
-1
Info.plist
examples/platform_view/macos/Runner/Info.plist
+1
-1
binding.dart
packages/flutter/lib/src/services/binding.dart
+97
-9
system_channels.dart
packages/flutter/lib/src/services/system_channels.dart
+8
-0
binding.dart
packages/flutter/lib/src/widgets/binding.dart
+30
-2
binding_test.dart
packages/flutter/test/services/binding_test.dart
+32
-0
Info.plist
...r_tools/templates/app_shared/macos.tmpl/Runner/Info.plist
+1
-1
No files found.
dev/benchmarks/complex_layout/macos/Runner/Info.plist
View file @
bcdab118
...
...
@@ -27,6 +27,6 @@
<
k
e
y
>
NSMainNibFile
<
/k
e
y
>
<
string
>
MainMenu
<
/string
>
<
k
e
y
>
NSPrincipalClass
<
/k
e
y
>
<
string
>
Flutter
Application
<
/string
>
<
string
>
NS
Application
<
/string
>
<
/
d
i
c
t
>
<
/plist
>
dev/benchmarks/macrobenchmarks/macos/Runner/Info.plist
View file @
bcdab118
...
...
@@ -27,6 +27,6 @@
<
k
e
y
>
NSMainNibFile
<
/k
e
y
>
<
string
>
MainMenu
<
/string
>
<
k
e
y
>
NSPrincipalClass
<
/k
e
y
>
<
string
>
Flutter
Application
<
/string
>
<
string
>
NS
Application
<
/string
>
<
/
d
i
c
t
>
<
/plist
>
dev/integration_tests/channels/macos/Runner/Info.plist
View file @
bcdab118
...
...
@@ -27,6 +27,6 @@
<
k
e
y
>
NSMainNibFile
<
/k
e
y
>
<
string
>
MainMenu
<
/string
>
<
k
e
y
>
NSPrincipalClass
<
/k
e
y
>
<
string
>
Flutter
Application
<
/string
>
<
string
>
NS
Application
<
/string
>
<
/
d
i
c
t
>
<
/plist
>
dev/integration_tests/flavors/macos/Runner/Info.plist
View file @
bcdab118
...
...
@@ -29,6 +29,6 @@
<
k
e
y
>
NSMainNibFile
<
/k
e
y
>
<
string
>
MainMenu
<
/string
>
<
k
e
y
>
NSPrincipalClass
<
/k
e
y
>
<
string
>
Flutter
Application
<
/string
>
<
string
>
NS
Application
<
/string
>
<
/
d
i
c
t
>
<
/plist
>
dev/integration_tests/flutter_gallery/macos/Runner/Info.plist
View file @
bcdab118
...
...
@@ -27,6 +27,6 @@
<
k
e
y
>
NSMainNibFile
<
/k
e
y
>
<
string
>
MainMenu
<
/string
>
<
k
e
y
>
NSPrincipalClass
<
/k
e
y
>
<
string
>
Flutter
Application
<
/string
>
<
string
>
NS
Application
<
/string
>
<
/
d
i
c
t
>
<
/plist
>
dev/integration_tests/ui/macos/Runner/Info.plist
View file @
bcdab118
...
...
@@ -27,6 +27,6 @@
<
k
e
y
>
NSMainNibFile
<
/k
e
y
>
<
string
>
MainMenu
<
/string
>
<
k
e
y
>
NSPrincipalClass
<
/k
e
y
>
<
string
>
Flutter
Application
<
/string
>
<
string
>
NS
Application
<
/string
>
<
/
d
i
c
t
>
<
/plist
>
dev/manual_tests/macos/Runner/Info.plist
View file @
bcdab118
...
...
@@ -27,6 +27,6 @@
<
k
e
y
>
NSMainNibFile
<
/k
e
y
>
<
string
>
MainMenu
<
/string
>
<
k
e
y
>
NSPrincipalClass
<
/k
e
y
>
<
string
>
Flutter
Application
<
/string
>
<
string
>
NS
Application
<
/string
>
<
/
d
i
c
t
>
<
/plist
>
examples/api/lib/services/binding/handle_request_app_exit.0.dart
0 → 100644
View file @
bcdab118
// Copyright 2014 The Flutter Authors. 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:ui'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/services.dart'
;
/// Flutter code sample for [ServicesBinding.handleRequestAppExit].
void
main
(
)
{
runApp
(
const
ApplicationExitExample
());
}
class
ApplicationExitExample
extends
StatelessWidget
{
const
ApplicationExitExample
({
super
.
key
});
@override
Widget
build
(
BuildContext
context
)
{
return
const
MaterialApp
(
home:
Scaffold
(
body:
Body
()),
);
}
}
class
Body
extends
StatefulWidget
{
const
Body
({
super
.
key
});
@override
State
<
Body
>
createState
()
=>
_BodyState
();
}
class
_BodyState
extends
State
<
Body
>
with
WidgetsBindingObserver
{
bool
_shouldExit
=
false
;
String
lastResponse
=
'No exit requested yet'
;
@override
void
initState
()
{
super
.
initState
();
WidgetsBinding
.
instance
.
addObserver
(
this
);
}
@override
void
dispose
()
{
WidgetsBinding
.
instance
.
removeObserver
(
this
);
super
.
dispose
();
}
Future
<
void
>
_quit
()
async
{
final
AppExitType
exitType
=
_shouldExit
?
AppExitType
.
required
:
AppExitType
.
cancelable
;
setState
(()
{
lastResponse
=
'App requesting
${exitType.name}
exit'
;
});
await
ServicesBinding
.
instance
.
exitApplication
(
exitType
);
}
@override
Future
<
AppExitResponse
>
didRequestAppExit
()
async
{
final
AppExitResponse
response
=
_shouldExit
?
AppExitResponse
.
exit
:
AppExitResponse
.
cancel
;
setState
(()
{
lastResponse
=
'App responded
${response.name}
to exit request'
;
});
return
response
;
}
void
_radioChanged
(
bool
?
value
)
{
value
??=
true
;
if
(
_shouldExit
==
value
)
{
return
;
}
setState
(()
{
_shouldExit
=
value
!;
});
}
@override
Widget
build
(
BuildContext
context
)
{
return
Center
(
child:
SizedBox
(
width:
300
,
child:
IntrinsicHeight
(
child:
Column
(
children:
<
Widget
>[
RadioListTile
<
bool
>(
title:
const
Text
(
'Do Not Allow Exit'
),
groupValue:
_shouldExit
,
value:
false
,
onChanged:
_radioChanged
,
),
RadioListTile
<
bool
>(
title:
const
Text
(
'Allow Exit'
),
groupValue:
_shouldExit
,
value:
true
,
onChanged:
_radioChanged
,
),
const
SizedBox
(
height:
30
),
ElevatedButton
(
onPressed:
_quit
,
child:
const
Text
(
'Quit'
),
),
const
SizedBox
(
height:
30
),
Text
(
lastResponse
),
],
),
),
),
);
}
}
examples/api/macos/Runner/Info.plist
View file @
bcdab118
...
...
@@ -27,6 +27,6 @@
<
k
e
y
>
NSMainNibFile
<
/k
e
y
>
<
string
>
MainMenu
<
/string
>
<
k
e
y
>
NSPrincipalClass
<
/k
e
y
>
<
string
>
Flutter
Application
<
/string
>
<
string
>
NS
Application
<
/string
>
<
/
d
i
c
t
>
<
/plist
>
examples/api/test/services/binding/handle_request_app_exit.0_test.dart
0 → 100644
View file @
bcdab118
// Copyright 2014 The Flutter Authors. 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_api_samples/services/binding/handle_request_app_exit.0.dart'
as
example
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
testWidgets
(
'Application Exit example'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
example
.
ApplicationExitExample
(),
);
expect
(
find
.
text
(
'No exit requested yet'
),
findsOneWidget
);
expect
(
find
.
text
(
'Do Not Allow Exit'
),
findsOneWidget
);
expect
(
find
.
text
(
'Allow Exit'
),
findsOneWidget
);
expect
(
find
.
text
(
'Quit'
),
findsOneWidget
);
await
tester
.
tap
(
find
.
text
(
'Quit'
));
await
tester
.
pump
();
expect
(
find
.
text
(
'App requesting cancelable exit'
),
findsOneWidget
);
});
}
examples/flutter_view/macos/Runner/Info.plist
View file @
bcdab118
...
...
@@ -27,6 +27,6 @@
<
k
e
y
>
NSMainNibFile
<
/k
e
y
>
<
string
>
MainMenu
<
/string
>
<
k
e
y
>
NSPrincipalClass
<
/k
e
y
>
<
string
>
Flutter
Application
<
/string
>
<
string
>
NS
Application
<
/string
>
<
/
d
i
c
t
>
<
/plist
>
examples/hello_world/macos/Runner/Info.plist
View file @
bcdab118
...
...
@@ -27,6 +27,6 @@
<
k
e
y
>
NSMainNibFile
<
/k
e
y
>
<
string
>
MainMenu
<
/string
>
<
k
e
y
>
NSPrincipalClass
<
/k
e
y
>
<
string
>
Flutter
Application
<
/string
>
<
string
>
NS
Application
<
/string
>
<
/
d
i
c
t
>
<
/plist
>
examples/image_list/macos/Runner/Info.plist
View file @
bcdab118
...
...
@@ -27,6 +27,6 @@
<
k
e
y
>
NSMainNibFile
<
/k
e
y
>
<
string
>
MainMenu
<
/string
>
<
k
e
y
>
NSPrincipalClass
<
/k
e
y
>
<
string
>
Flutter
Application
<
/string
>
<
string
>
NS
Application
<
/string
>
<
/
d
i
c
t
>
<
/plist
>
examples/layers/macos/Runner/Info.plist
View file @
bcdab118
...
...
@@ -27,6 +27,6 @@
<
k
e
y
>
NSMainNibFile
<
/k
e
y
>
<
string
>
MainMenu
<
/string
>
<
k
e
y
>
NSPrincipalClass
<
/k
e
y
>
<
string
>
Flutter
Application
<
/string
>
<
string
>
NS
Application
<
/string
>
<
/
d
i
c
t
>
<
/plist
>
examples/platform_view/macos/Runner/Info.plist
View file @
bcdab118
...
...
@@ -27,6 +27,6 @@
<
k
e
y
>
NSMainNibFile
<
/k
e
y
>
<
string
>
MainMenu
<
/string
>
<
k
e
y
>
NSPrincipalClass
<
/k
e
y
>
<
string
>
Flutter
Application
<
/string
>
<
string
>
NS
Application
<
/string
>
<
/
d
i
c
t
>
<
/plist
>
packages/flutter/lib/src/services/binding.dart
View file @
bcdab118
...
...
@@ -263,30 +263,119 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
return
null
;
}
Future
<
void
>
_handlePlatformMessage
(
MethodCall
methodCall
)
async
{
Future
<
dynamic
>
_handlePlatformMessage
(
MethodCall
methodCall
)
async
{
final
String
method
=
methodCall
.
method
;
// There is only one incoming method call currently possible.
assert
(
method
==
'SystemChrome.systemUIChange'
);
final
List
<
dynamic
>
args
=
methodCall
.
arguments
as
List
<
dynamic
>;
if
(
_systemUiChangeCallback
!=
null
)
{
await
_systemUiChangeCallback
!(
args
[
0
]
as
bool
);
assert
(
method
==
'SystemChrome.systemUIChange'
||
method
==
'System.requestAppExit'
);
switch
(
method
)
{
case
'SystemChrome.systemUIChange'
:
final
List
<
dynamic
>
args
=
methodCall
.
arguments
as
List
<
dynamic
>;
if
(
_systemUiChangeCallback
!=
null
)
{
await
_systemUiChangeCallback
!(
args
[
0
]
as
bool
);
}
break
;
case
'System.requestAppExit'
:
return
<
String
,
dynamic
>{
'response'
:
(
await
handleRequestAppExit
()).
name
};
}
}
static
AppLifecycleState
?
_parseAppLifecycleMessage
(
String
message
)
{
switch
(
message
)
{
case
'AppLifecycleState.paused'
:
return
AppLifecycleState
.
paused
;
case
'AppLifecycleState.resumed'
:
return
AppLifecycleState
.
resumed
;
case
'AppLifecycleState.inactive'
:
return
AppLifecycleState
.
inactive
;
case
'AppLifecycleState.paused'
:
return
AppLifecycleState
.
paused
;
case
'AppLifecycleState.detached'
:
return
AppLifecycleState
.
detached
;
}
return
null
;
}
/// Handles any requests for application exit that may be received on the
/// [SystemChannels.platform] method channel.
///
/// By default, returns [ui.AppExitResponse.exit].
///
/// {@template flutter.services.binding.ServicesBinding.requestAppExit}
/// Not all exits are cancelable, so not all exits will call this function. Do
/// not rely on this function as a place to save critical data, because you
/// will be disappointed. There are a number of ways that the application can
/// exit without letting the application know first: power can be unplugged,
/// the battery removed, the application can be killed in a task manager or
/// command line, or the device could have a rapid unplanned disassembly (i.e.
/// it could explode). In all of those cases (and probably others), no
/// notification will be given to the application that it is about to exit.
/// {@endtemplate}
///
/// {@tool sample}
/// This examples shows how an application can cancel (or not) OS requests for
/// quitting an application. Currently this is only supported on macOS and
/// Linux.
///
/// ** See code in examples/api/lib/services/binding/handle_request_app_exit.0.dart **
/// {@end-tool}
///
/// See also:
///
/// * [WidgetsBindingObserver.didRequestAppExit], which can be overridden to
/// respond to this message.
/// * [WidgetsBinding.handleRequestAppExit] which overrides this method to
/// notify its observers.
Future
<
ui
.
AppExitResponse
>
handleRequestAppExit
()
async
{
return
ui
.
AppExitResponse
.
exit
;
}
/// Exits the application by calling the native application API method for
/// exiting an application cleanly.
///
/// This differs from calling `dart:io`'s [exit] function in that it gives the
/// engine a chance to clean up resources so that it doesn't crash on exit, so
/// calling this is always preferred over calling [exit]. It also optionally
/// gives handlers of [handleRequestAppExit] a chance to cancel the
/// application exit.
///
/// The [exitType] indicates what kind of exit to perform. For
/// [ui.AppExitType.cancelable] exits, the application is queried through a
/// call to [handleRequestAppExit], where the application can optionally
/// cancel the request for exit. If the [exitType] is
/// [ui.AppExitType.required], then the application exits immediately without
/// querying the application.
///
/// For [ui.AppExitType.cancelable] exits, the returned response value is the
/// response obtained from the application as to whether the exit was canceled
/// or not. Practically, the response will never be [ui.AppExitResponse.exit],
/// since the application will have already exited by the time the result
/// would have been received.
///
/// The optional [exitCode] argument will be used as the application exit code
/// on platforms where an exit code is supported. On other platforms it may be
/// ignored. It defaults to zero.
///
/// See also:
///
/// * [WidgetsBindingObserver.didRequestAppExit] for a handler you can
/// override on a [WidgetsBindingObserver] to receive exit requests.
@mustCallSuper
Future
<
ui
.
AppExitResponse
>
exitApplication
(
ui
.
AppExitType
exitType
,
[
int
exitCode
=
0
])
async
{
final
Map
<
String
,
Object
?>?
result
=
await
SystemChannels
.
platform
.
invokeMethod
<
Map
<
String
,
Object
?>>(
'System.exitApplication'
,
<
String
,
Object
?>{
'type'
:
exitType
.
name
,
'exitCode'
:
exitCode
},
);
if
(
result
==
null
)
{
return
ui
.
AppExitResponse
.
cancel
;
}
switch
(
result
[
'response'
])
{
case
'cancel'
:
return
ui
.
AppExitResponse
.
cancel
;
case
'exit'
:
default
:
// In practice, this will never get returned, because the application
// will have exited before it returns.
return
ui
.
AppExitResponse
.
exit
;
}
}
/// The [RestorationManager] synchronizes the restoration data between
/// engine and framework.
///
...
...
@@ -326,7 +415,6 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
void
setSystemUiChangeCallback
(
SystemUiChangeCallback
?
callback
)
{
_systemUiChangeCallback
=
callback
;
}
}
/// Signature for listening to changes in the [SystemUiMode].
...
...
packages/flutter/lib/src/services/system_channels.dart
View file @
bcdab118
...
...
@@ -120,6 +120,11 @@ class SystemChannels {
/// * `SystemNavigator.pop`: Tells the operating system to close the
/// application, or the closest equivalent. See [SystemNavigator.pop].
///
/// * `System.exitApplication`: Tells the engine to send a request back to
/// the application to request an application exit (using
/// `System.requestAppExit` below), and if it is not canceled, to terminate
/// the application using the platform UI toolkit's termination API.
///
/// The following incoming methods are defined for this channel (registered
/// using [MethodChannel.setMethodCallHandler]):
///
...
...
@@ -129,6 +134,9 @@ class SystemChannels {
/// [SystemChrome.setSystemUIChangeCallback] to respond to this change in
/// application state.
///
/// * `System.requestAppExit`: The application has requested that it be
/// terminated. See [ServicesBinding.exitApplication].
///
/// Calls to methods that are not implemented on the shell side are ignored
/// (so it is safe to call methods when the relevant plugin might be missing).
static
const
MethodChannel
platform
=
OptionalMethodChannel
(
...
...
packages/flutter/lib/src/widgets/binding.dart
View file @
bcdab118
...
...
@@ -4,7 +4,7 @@
import
'dart:async'
;
import
'dart:developer'
as
developer
;
import
'dart:ui'
show
AccessibilityFeatures
,
AppLifecycleState
,
FrameTiming
,
Locale
,
PlatformDispatcher
,
TimingsCallback
;
import
'dart:ui'
show
AccessibilityFeatures
,
App
ExitResponse
,
App
LifecycleState
,
FrameTiming
,
Locale
,
PlatformDispatcher
,
TimingsCallback
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/gestures.dart'
;
...
...
@@ -41,7 +41,6 @@ export 'dart:ui' show AppLifecycleState, Locale;
/// lifecycle messages. See [didChangeAppLifecycleState].
///
/// ** See code in examples/api/lib/widgets/binding/widget_binding_observer.0.dart **
///
/// {@end-tool}
///
/// To respond to other notifications, replace the [didChangeAppLifecycleState]
...
...
@@ -228,6 +227,21 @@ abstract class WidgetsBindingObserver {
/// This method exposes notifications from [SystemChannels.lifecycle].
void
didChangeAppLifecycleState
(
AppLifecycleState
state
)
{
}
/// Called when a request is received from the system to exit the application.
///
/// If any observer responds with [AppExitResponse.cancel], it will cancel the
/// exit. All observers will be asked before exiting.
///
/// {@macro flutter.services.binding.ServicesBinding.requestAppExit}
///
/// See also:
///
/// * [ServicesBinding.exitApplication] for a function to call that will request
/// that the application exits.
Future
<
AppExitResponse
>
didRequestAppExit
()
async
{
return
AppExitResponse
.
exit
;
}
/// Called when the system is running low on memory.
///
/// This method exposes the `memoryPressure` notification from
...
...
@@ -526,6 +540,20 @@ mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureB
/// * [WidgetsBindingObserver], which has an example of using this method.
bool
removeObserver
(
WidgetsBindingObserver
observer
)
=>
_observers
.
remove
(
observer
);
@override
Future
<
AppExitResponse
>
handleRequestAppExit
()
async
{
bool
didCancel
=
false
;
for
(
final
WidgetsBindingObserver
observer
in
_observers
)
{
if
((
await
observer
.
didRequestAppExit
())
==
AppExitResponse
.
cancel
)
{
didCancel
=
true
;
// Don't early return. For the case where someone is just using the
// observer to know when exit happens, we want to call all the
// observers, even if we already know we're going to cancel.
}
}
return
didCancel
?
AppExitResponse
.
cancel
:
AppExitResponse
.
exit
;
}
@override
void
handleMetricsChanged
()
{
super
.
handleMetricsChanged
();
...
...
packages/flutter/test/services/binding_test.dart
View file @
bcdab118
...
...
@@ -4,6 +4,7 @@
import
'dart:convert'
;
import
'dart:io'
;
import
'dart:ui'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/scheduler.dart'
;
...
...
@@ -113,4 +114,35 @@ void main() {
expect
(
data
,
isNotNull
);
});
});
test
(
'Calling exitApplication sends a method call to the engine'
,
()
async
{
bool
sentMessage
=
false
;
MethodCall
?
methodCall
;
binding
.
defaultBinaryMessenger
.
setMockMessageHandler
(
'flutter/platform'
,
(
ByteData
?
message
)
async
{
methodCall
=
const
JSONMethodCodec
().
decodeMethodCall
(
message
);
sentMessage
=
true
;
return
const
JSONMethodCodec
().
encodeSuccessEnvelope
(<
String
,
String
>{
'response'
:
'cancel'
});
});
final
AppExitResponse
response
=
await
binding
.
exitApplication
(
AppExitType
.
required
);
expect
(
sentMessage
,
isTrue
);
expect
(
methodCall
,
isNotNull
);
expect
((
methodCall
!.
arguments
as
Map
<
String
,
dynamic
>)[
'type'
],
equals
(
'required'
));
expect
(
response
,
equals
(
AppExitResponse
.
cancel
));
});
test
(
'Default handleRequestAppExit returns exit'
,
()
async
{
const
MethodCall
incomingCall
=
MethodCall
(
'System.requestAppExit'
,
<
dynamic
>[<
String
,
dynamic
>{
'type'
:
'cancelable'
}]);
bool
receivedReply
=
false
;
Map
<
String
,
dynamic
>?
result
;
await
binding
.
defaultBinaryMessenger
.
handlePlatformMessage
(
'flutter/platform'
,
const
JSONMethodCodec
().
encodeMethodCall
(
incomingCall
),
(
ByteData
?
message
)
async
{
result
=
(
const
JSONMessageCodec
().
decodeMessage
(
message
)
as
List
<
dynamic
>)[
0
]
as
Map
<
String
,
dynamic
>;
receivedReply
=
true
;
},
);
expect
(
receivedReply
,
isTrue
);
expect
(
result
,
isNotNull
);
expect
(
result
![
'response'
],
equals
(
'exit'
));
});
}
packages/flutter_tools/templates/app_shared/macos.tmpl/Runner/Info.plist
View file @
bcdab118
...
...
@@ -27,6 +27,6 @@
<
k
e
y
>
NSMainNibFile
<
/k
e
y
>
<
string
>
MainMenu
<
/string
>
<
k
e
y
>
NSPrincipalClass
<
/k
e
y
>
<
string
>
Flutter
Application
<
/string
>
<
string
>
NS
Application
<
/string
>
<
/
d
i
c
t
>
<
/plist
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment