Performance Debugging
continuous-integration/drone/pr Build is passing Details

This commit is contained in:
Sarah Jamie Lewis 2021-05-25 13:43:13 -07:00
parent 7fba47a1e0
commit 05779c49e2
13 changed files with 246 additions and 142 deletions

View File

@ -1,4 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'package:cwtch/notification_manager.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:cwtch/torstatus.dart'; import 'package:cwtch/torstatus.dart';
@ -14,12 +15,14 @@ class CwtchNotifier {
late Settings settings; late Settings settings;
late ErrorHandler error; late ErrorHandler error;
late TorStatus torStatus; late TorStatus torStatus;
late NotificationsManager notificationManager;
CwtchNotifier(ProfileListState pcn, Settings settingsCN, ErrorHandler errorCN, TorStatus torStatusCN) { CwtchNotifier(ProfileListState pcn, Settings settingsCN, ErrorHandler errorCN, TorStatus torStatusCN, NotificationsManager notificationManagerP) {
profileCN = pcn; profileCN = pcn;
settings = settingsCN; settings = settingsCN;
error = errorCN; error = errorCN;
torStatus = torStatusCN; torStatus = torStatusCN;
notificationManager = notificationManagerP;
} }
void handleMessage(String type, dynamic data) { void handleMessage(String type, dynamic data) {
@ -60,6 +63,7 @@ class CwtchNotifier {
} }
break; break;
case "NewMessageFromPeer": case "NewMessageFromPeer":
notificationManager.notify("New Message From Peer!");
profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["RemotePeer"]).unreadMessages++; profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["RemotePeer"]).unreadMessages++;
profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["RemotePeer"]).totalMessages++; profileCN.getProfile(data["ProfileOnion"]).contactList.getContact(data["RemotePeer"]).totalMessages++;
profileCN.getProfile(data["ProfileOnion"]).contactList.updateLastMessageTime(data["RemotePeer"], DateTime.now()); profileCN.getProfile(data["ProfileOnion"]).contactList.updateLastMessageTime(data["RemotePeer"], DateTime.now());

View File

@ -1,3 +1,4 @@
import 'package:cwtch/notification_manager.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:cwtch/cwtch/ffi.dart'; import 'package:cwtch/cwtch/ffi.dart';
import 'package:cwtch/cwtch/gomobile.dart'; import 'package:cwtch/cwtch/gomobile.dart';
@ -50,11 +51,15 @@ class FlwtchState extends State<Flwtch> {
cwtchInit = false; cwtchInit = false;
profs = ProfileListState(); profs = ProfileListState();
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus);
if (Platform.isAndroid) { if (Platform.isAndroid) {
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager());
cwtch = CwtchGomobile(cwtchNotifier); cwtch = CwtchGomobile(cwtchNotifier);
} else if (Platform.isLinux) {
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, LinuxNotificationsManager());
cwtch = CwtchFfi(cwtchNotifier);
} else { } else {
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager());
cwtch = CwtchFfi(cwtchNotifier); cwtch = CwtchFfi(cwtchNotifier);
} }
@ -78,7 +83,13 @@ class FlwtchState extends State<Flwtch> {
//appStatus = AppModel(cwtch: cwtch); //appStatus = AppModel(cwtch: cwtch);
return MultiProvider( return MultiProvider(
providers: [getFlwtchStateProvider(), getProfileListProvider(), getSettingsProvider(), getErrorHandlerProvider(), getTorStatusProvider()], providers: [
getFlwtchStateProvider(),
getProfileListProvider(),
getSettingsProvider(),
getErrorHandlerProvider(),
getTorStatusProvider(),
],
builder: (context, widget) { builder: (context, widget) {
Provider.of<Settings>(context).initPackageInfo(); Provider.of<Settings>(context).initPackageInfo();
return Consumer<Settings>( return Consumer<Settings>(

View File

@ -0,0 +1,26 @@
import 'package:desktop_notifications/desktop_notifications.dart';
import 'package:path/path.dart' as path;
// NotificationsManager provides a wrapper around platform specific notifications logic.
abstract class NotificationsManager {
Future<void> notify(String message);
}
// NullNotificationsManager ignores all notification requests
class NullNotificationsManager implements NotificationsManager {
@override
Future<void> notify(String message) async {}
}
// LinuxNotificationsManager uses the desktop_notifications package to implement
// the standard dbus-powered linux desktop notifications.
class LinuxNotificationsManager implements NotificationsManager {
int previous_id = 0;
LinuxNotificationsManager() {}
Future<void> notify(String message) async {
var client = NotificationsClient();
var icon_path = Uri.file(path.join(path.current, "cwtch.png"));
client.notify('New Message from Peer!', appName: "cwtch", appIcon: icon_path.toString(), replacesId: this.previous_id).then((Notification value) => previous_id = value.id);
client.close();
}
}

View File

@ -31,48 +31,50 @@ class _ContactsViewState extends State<ContactsView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( endDrawerEnableOpenDragGesture: false,
title: Row(children: [ drawerEnableOpenDragGesture: false,
ProfileImage( appBar: AppBar(
imagePath: Provider.of<ProfileInfoState>(context).imagePath, title: RepaintBoundary(
diameter: 42, child: Row(children: [
border: Provider.of<Settings>(context).theme.portraitOnlineBorderColor(), ProfileImage(
badgeTextColor: Colors.red, imagePath: Provider.of<ProfileInfoState>(context).imagePath,
badgeColor: Colors.red, diameter: 42,
), border: Provider.of<Settings>(context).theme.portraitOnlineBorderColor(),
SizedBox( badgeTextColor: Colors.red,
width: 10, badgeColor: Colors.red,
), ),
Expanded( SizedBox(
child: Text( width: 10,
"%1 » %2".replaceAll("%1", Provider.of<ProfileInfoState>(context).nickname).replaceAll("%2", "Contacts"), ),
overflow: TextOverflow.ellipsis, Expanded(
)), //todo child: Text(
]), "%1 » %2".replaceAll("%1", Provider.of<ProfileInfoState>(context).nickname).replaceAll("%2", "Contacts"),
actions: [ overflow: TextOverflow.ellipsis,
IconButton(icon: TorIcon(), onPressed: _pushTorStatus), )), //todo
IconButton( ])),
icon: Icon(Icons.copy), actions: [
onPressed: _copyOnion, IconButton(icon: TorIcon(), onPressed: _pushTorStatus),
), IconButton(
IconButton( icon: Icon(Icons.copy),
// need both conditions for displaying initial empty textfield and also allowing filters to be cleared if this widget gets lost/reset onPressed: _copyOnion,
icon: Icon(showSearchBar || Provider.of<ContactListState>(context).isFiltered ? Icons.search_off : Icons.search), ),
onPressed: () { IconButton(
Provider.of<ContactListState>(context, listen: false).filter = ""; // need both conditions for displaying initial empty textfield and also allowing filters to be cleared if this widget gets lost/reset
setState(() { icon: Icon(showSearchBar || Provider.of<ContactListState>(context).isFiltered ? Icons.search_off : Icons.search),
showSearchBar = !showSearchBar; onPressed: () {
}); Provider.of<ContactListState>(context, listen: false).filter = "";
}) setState(() {
], showSearchBar = !showSearchBar;
), });
floatingActionButton: FloatingActionButton( })
onPressed: _pushAddContact, ],
tooltip: AppLocalizations.of(context)!.tooltipAddContact, ),
child: const Icon(Icons.person_add_sharp), floatingActionButton: FloatingActionButton(
), onPressed: _pushAddContact,
body: showSearchBar || Provider.of<ContactListState>(context).isFiltered ? _buildFilterable() : _buildContactList(), tooltip: AppLocalizations.of(context)!.tooltipAddContact,
); child: const Icon(Icons.person_add_sharp),
),
body: showSearchBar || Provider.of<ContactListState>(context).isFiltered ? _buildFilterable() : _buildContactList());
} }
Widget _buildFilterable() { Widget _buildFilterable() {
@ -89,13 +91,13 @@ class _ContactsViewState extends State<ContactsView> {
Widget _buildContactList() { Widget _buildContactList() {
final tiles = Provider.of<ContactListState>(context).contacts.map((ContactInfoState contact) { final tiles = Provider.of<ContactListState>(context).contacts.map((ContactInfoState contact) {
return ChangeNotifierProvider<ContactInfoState>.value(key: ValueKey(contact.profileOnion + "" + contact.onion), value: contact, builder: (_, __) => ContactRow()); return ChangeNotifierProvider<ContactInfoState>.value(key: ValueKey(contact.profileOnion + "" + contact.onion), value: contact, builder: (_, __) => RepaintBoundary(child: ContactRow()));
}); });
final divided = ListTile.divideTiles( final divided = ListTile.divideTiles(
context: context, context: context,
tiles: tiles, tiles: tiles,
).toList(); ).toList();
return ListView(children: divided); return RepaintBoundary(child: ListView(children: divided));
} }
void _pushAddContact() { void _pushAddContact() {

View File

@ -113,42 +113,43 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
showModalBottomSheet<void>( showModalBottomSheet<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return Container( return RepaintBoundary(
height: 200, // bespoke value courtesy of the [TextField] docs child: Container(
child: Center( height: 200, // bespoke value courtesy of the [TextField] docs
child: Padding( child: Center(
padding: EdgeInsets.all(10.0), child: Padding(
child: Column( padding: EdgeInsets.all(10.0),
mainAxisAlignment: MainAxisAlignment.center, child: Column(
mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ mainAxisSize: MainAxisSize.min,
Text(AppLocalizations.of(context)!.enterProfilePassword), children: <Widget>[
SizedBox( Text(AppLocalizations.of(context)!.enterProfilePassword),
height: 20, SizedBox(
), height: 20,
CwtchPasswordField( ),
controller: ctrlrPassword, CwtchPasswordField(
validator: (value) {}, controller: ctrlrPassword,
), validator: (value) {},
SizedBox( ),
height: 20, SizedBox(
), height: 20,
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ),
Spacer(), Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
Expanded( Spacer(),
child: ElevatedButton( Expanded(
child: Text(AppLocalizations.of(context)!.unlock, semanticsLabel: AppLocalizations.of(context)!.unlock), child: ElevatedButton(
onPressed: () { child: Text(AppLocalizations.of(context)!.unlock, semanticsLabel: AppLocalizations.of(context)!.unlock),
Provider.of<FlwtchState>(context, listen: false).cwtch.LoadProfiles(ctrlrPassword.value.text); onPressed: () {
ctrlrPassword.text = ""; Provider.of<FlwtchState>(context, listen: false).cwtch.LoadProfiles(ctrlrPassword.value.text);
Navigator.pop(context); ctrlrPassword.text = "";
}, Navigator.pop(context);
)), },
Spacer() )),
]), Spacer()
], ]),
)), ],
)); )),
)));
}); });
} }
@ -157,7 +158,7 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
(ProfileInfoState profile) { (ProfileInfoState profile) {
return ChangeNotifierProvider<ProfileInfoState>.value( return ChangeNotifierProvider<ProfileInfoState>.value(
value: profile, value: profile,
builder: (context, child) => ProfileRow(), builder: (context, child) => RepaintBoundary(child: ProfileRow()),
); );
}, },
); );

View File

@ -71,26 +71,27 @@ class MessageBubbleState extends State<MessageBubble> {
return LayoutBuilder(builder: (context, constraints) { return LayoutBuilder(builder: (context, constraints) {
//print(constraints.toString()+", "+constraints.maxWidth.toString()); //print(constraints.toString()+", "+constraints.maxWidth.toString());
return Container( return RepaintBoundary(
child: Container( child: Container(
decoration: BoxDecoration( child: Container(
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor() : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor(), decoration: BoxDecoration(
border: color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor() : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor(),
Border.all(color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor() : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor(), width: 1), border: Border.all(
borderRadius: BorderRadius.only( color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor() : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor(), width: 1),
topLeft: Radius.circular(borderRadiousEh), borderRadius: BorderRadius.only(
topRight: Radius.circular(borderRadiousEh), topLeft: Radius.circular(borderRadiousEh),
bottomLeft: fromMe ? Radius.circular(borderRadiousEh) : Radius.zero, topRight: Radius.circular(borderRadiousEh),
bottomRight: fromMe ? Radius.zero : Radius.circular(borderRadiousEh), bottomLeft: fromMe ? Radius.circular(borderRadiousEh) : Radius.zero,
), bottomRight: fromMe ? Radius.zero : Radius.circular(borderRadiousEh),
), ),
child: Padding( ),
padding: EdgeInsets.all(9.0), child: Padding(
child: Column( padding: EdgeInsets.all(9.0),
crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, child: Column(
mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start, crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start,
children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations])))); mainAxisSize: MainAxisSize.min,
children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations])))));
}); });
} }
} }

View File

@ -15,41 +15,42 @@ class _MessageListState extends State<MessageList> {
@override @override
Widget build(BuildContext outerContext) { Widget build(BuildContext outerContext) {
return Container( return RepaintBoundary(
child: Scrollbar( child: Container(
isAlwaysShown: true, child: Scrollbar(
controller: ctrlr1, isAlwaysShown: true,
child: Container(
// Only show broken heart is the contact is offline...
decoration: BoxDecoration(
image: Provider.of<ContactInfoState>(outerContext).isOnline()
? null
: DecorationImage(
fit: BoxFit.contain,
image: AssetImage("assets/core/negative_heart_512px.png"),
colorFilter: ColorFilter.mode(Provider.of<Settings>(context).theme.mainTextColor(), BlendMode.srcIn))),
child: ListView.builder(
controller: ctrlr1, controller: ctrlr1,
itemCount: Provider.of<ContactInfoState>(outerContext).totalMessages, child: Container(
reverse: true, // Only show broken heart is the contact is offline...
itemBuilder: (itemBuilderContext, index) { decoration: BoxDecoration(
var trueIndex = Provider.of<ContactInfoState>(outerContext).totalMessages - index - 1; image: Provider.of<ContactInfoState>(outerContext).isOnline()
return ChangeNotifierProvider( ? null
key: ValueKey(trueIndex), : DecorationImage(
create: (x) => MessageState( fit: BoxFit.contain,
context: itemBuilderContext, image: AssetImage("assets/core/negative_heart_512px.png"),
profileOnion: Provider.of<ProfileInfoState>(outerContext, listen: false).onion, colorFilter: ColorFilter.mode(Provider.of<Settings>(context).theme.mainTextColor(), BlendMode.srcIn))),
contactHandle: Provider.of<ContactInfoState>(x, listen: false).onion, child: ListView.builder(
messageIndex: trueIndex, controller: ctrlr1,
), itemCount: Provider.of<ContactInfoState>(outerContext).totalMessages,
builder: (bcontext, child) { reverse: true,
String idx = Provider.of<ContactInfoState>(outerContext).isGroup == true && Provider.of<MessageState>(bcontext).signature.isEmpty == false itemBuilder: (itemBuilderContext, index) {
? Provider.of<MessageState>(bcontext).signature var trueIndex = Provider.of<ContactInfoState>(outerContext).totalMessages - index - 1;
: trueIndex.toString(); return ChangeNotifierProvider(
return MessageRow(key: Provider.of<ContactInfoState>(bcontext).getMessageKey(idx)); key: ValueKey(trueIndex),
}); create: (x) => MessageState(
}, context: itemBuilderContext,
), profileOnion: Provider.of<ProfileInfoState>(outerContext, listen: false).onion,
))); contactHandle: Provider.of<ContactInfoState>(x, listen: false).onion,
messageIndex: trueIndex,
),
builder: (bcontext, child) {
String idx = Provider.of<ContactInfoState>(outerContext).isGroup == true && Provider.of<MessageState>(bcontext).signature.isEmpty == false
? Provider.of<MessageState>(bcontext).signature
: trueIndex.toString();
return RepaintBoundary(child: MessageRow(key: Provider.of<ContactInfoState>(bcontext).getMessageKey(idx)));
});
},
),
))));
} }
} }

View File

@ -21,7 +21,8 @@ class ProfileImage extends StatefulWidget {
class _ProfileImageState extends State<ProfileImage> { class _ProfileImageState extends State<ProfileImage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Stack(children: [ return RepaintBoundary(
child: Stack(children: [
ClipOval( ClipOval(
clipBehavior: Clip.antiAlias, clipBehavior: Clip.antiAlias,
child: Container( child: Container(
@ -57,6 +58,6 @@ class _ProfileImageState extends State<ProfileImage> {
child: Text(widget.badgeCount > 99 ? "99+" : widget.badgeCount.toString(), style: TextStyle(color: widget.badgeTextColor, fontSize: 8.0)), child: Text(widget.badgeCount > 99 ? "99+" : widget.badgeCount.toString(), style: TextStyle(color: widget.badgeTextColor, fontSize: 8.0)),
), ),
)), )),
]); ]));
} }
} }

View File

@ -15,7 +15,8 @@ class TorIcon extends StatefulWidget {
class _TorIconState extends State<TorIcon> { class _TorIconState extends State<TorIcon> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Image( return RepaintBoundary(
child: Image(
image: AssetImage(Provider.of<TorStatus>(context).progress == 0 image: AssetImage(Provider.of<TorStatus>(context).progress == 0
? "assets/core/Tor_OFF.png" ? "assets/core/Tor_OFF.png"
: (Provider.of<TorStatus>(context).progress == 100 ? "assets/core/Tor_icon.png" : "assets/core/Tor_Booting_up.png")), : (Provider.of<TorStatus>(context).progress == 100 ? "assets/core/Tor_icon.png" : "assets/core/Tor_Booting_up.png")),
@ -25,6 +26,6 @@ class _TorIconState extends State<TorIcon> {
semanticLabel: Provider.of<TorStatus>(context).progress == 100 semanticLabel: Provider.of<TorStatus>(context).progress == 100
? AppLocalizations.of(context)!.networkStatusOnline ? AppLocalizations.of(context)!.networkStatusOnline
: (Provider.of<TorStatus>(context).progress == 0 ? AppLocalizations.of(context)!.networkStatusDisconnected : AppLocalizations.of(context)!.networkStatusAttemptingTor), : (Provider.of<TorStatus>(context).progress == 0 ? AppLocalizations.of(context)!.networkStatusDisconnected : AppLocalizations.of(context)!.networkStatusAttemptingTor),
); ));
} }
} }

View File

@ -4,6 +4,10 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <window_size/window_size_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) window_size_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowSizePlugin");
window_size_plugin_register_with_registrar(window_size_registrar);
} }

View File

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
window_size
) )
set(PLUGIN_BUNDLED_LIBRARIES) set(PLUGIN_BUNDLED_LIBRARIES)

View File

@ -8,6 +8,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
args:
dependency: transitive
description:
name: args
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
async: async:
dependency: transitive dependency: transitive
description: description:
@ -64,6 +71,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.3" version: "1.0.3"
dbus:
dependency: transitive
description:
name: dbus
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.0"
desktop_notifications:
dependency: "direct main"
description:
name: desktop_notifications
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.0"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -269,6 +290,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.11.0" version: "1.11.0"
petitparser:
dependency: transitive
description:
name: petitparser
url: "https://pub.dartlang.org"
source: hosted
version: "4.1.0"
platform: platform:
dependency: transitive dependency: transitive
description: description:
@ -386,6 +414,15 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.1" version: "2.1.1"
window_size:
dependency: "direct main"
description:
path: "plugins/window_size"
ref: e48abe7c3e9ebfe0b81622167c5201d4e783bb81
resolved-ref: e48abe7c3e9ebfe0b81622167c5201d4e783bb81
url: "git://github.com/google/flutter-desktop-embedding.git"
source: git
version: "0.1.0"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@ -393,6 +430,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.0" version: "0.2.0"
xml:
dependency: transitive
description:
name: xml
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.1"
sdks: sdks:
dart: ">=2.13.0 <3.0.0" dart: ">=2.13.0 <3.0.0"
flutter: ">=1.20.0" flutter: ">=1.20.0"

View File

@ -34,6 +34,7 @@ dependencies:
cupertino_icons: ^1.0.0 cupertino_icons: ^1.0.0
ffi: ^1.0.0 ffi: ^1.0.0
path_provider: ^2.0.0 path_provider: ^2.0.0
desktop_notifications: 0.5.0
glob: any glob: any
# todo: flutter_driver causes version conflict. eg https://github.com/flutter/flutter/issues/44829 # todo: flutter_driver causes version conflict. eg https://github.com/flutter/flutter/issues/44829
@ -44,6 +45,12 @@ dependencies:
flutter_driver: flutter_driver:
sdk: flutter sdk: flutter
window_size:
git:
url: git://github.com/google/flutter-desktop-embedding.git
path: plugins/window_size
ref: e48abe7c3e9ebfe0b81622167c5201d4e783bb81
#dev_dependencies: #dev_dependencies:
# flutter_lokalise: any # flutter_lokalise: any