Merge pull request #1305 from 21pages/tabbar

remove tabbar animation
This commit is contained in:
RustDesk 2022-08-18 22:24:19 +08:00 committed by GitHub
commit 54c5b6df1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 260 additions and 199 deletions

View File

@ -20,28 +20,28 @@ class ConnectionTabPage extends StatefulWidget {
State<ConnectionTabPage> createState() => _ConnectionTabPageState(params); State<ConnectionTabPage> createState() => _ConnectionTabPageState(params);
} }
class _ConnectionTabPageState extends State<ConnectionTabPage> class _ConnectionTabPageState extends State<ConnectionTabPage> {
with TickerProviderStateMixin {
// refactor List<int> when using multi-tab // refactor List<int> when using multi-tab
// this singleton is only for test // this singleton is only for test
RxList<TabInfo> tabs = RxList<TabInfo>.empty(growable: true); RxList<TabInfo> tabs = RxList<TabInfo>.empty(growable: true);
late Rx<TabController> tabController;
static final Rx<int> _selected = 0.obs;
static final Rx<String> _fullscreenID = "".obs; static final Rx<String> _fullscreenID = "".obs;
IconData icon = Icons.desktop_windows_sharp; final IconData selectedIcon = Icons.desktop_windows_sharp;
final IconData unselectedIcon = Icons.desktop_windows_outlined;
var connectionMap = RxList<Widget>.empty(growable: true); var connectionMap = RxList<Widget>.empty(growable: true);
_ConnectionTabPageState(Map<String, dynamic> params) { _ConnectionTabPageState(Map<String, dynamic> params) {
if (params['id'] != null) { if (params['id'] != null) {
tabs.add(TabInfo(label: params['id'], icon: icon)); tabs.add(TabInfo(
label: params['id'],
selectedIcon: selectedIcon,
unselectedIcon: unselectedIcon));
} }
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
tabController = TabController(length: tabs.length, vsync: this).obs;
rustDeskWinManager.setMethodHandler((call, fromWindowId) async { rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
print( print(
"call ${call.method} with args ${call.arguments} from window ${fromWindowId}"); "call ${call.method} with args ${call.arguments} from window ${fromWindowId}");
@ -50,8 +50,12 @@ class _ConnectionTabPageState extends State<ConnectionTabPage>
final args = jsonDecode(call.arguments); final args = jsonDecode(call.arguments);
final id = args['id']; final id = args['id'];
window_on_top(windowId()); window_on_top(windowId());
DesktopTabBar.onAdd(this, tabController, tabs, _selected, DesktopTabBar.onAdd(
TabInfo(label: id, icon: icon)); tabs,
TabInfo(
label: id,
selectedIcon: selectedIcon,
unselectedIcon: unselectedIcon));
} else if (call.method == "onDestroy") { } else if (call.method == "onDestroy") {
print( print(
"executing onDestroy hook, closing ${tabs.map((tab) => tab.label).toList()}"); "executing onDestroy hook, closing ${tabs.map((tab) => tab.label).toList()}");
@ -74,18 +78,16 @@ class _ConnectionTabPageState extends State<ConnectionTabPage>
Obx(() => Visibility( Obx(() => Visibility(
visible: _fullscreenID.value.isEmpty, visible: _fullscreenID.value.isEmpty,
child: DesktopTabBar( child: DesktopTabBar(
controller: tabController,
tabs: tabs, tabs: tabs,
onTabClose: onRemoveId, onTabClose: onRemoveId,
selected: _selected,
dark: isDarkTheme(), dark: isDarkTheme(),
mainTab: false, mainTab: false,
))), ))),
Expanded(child: Obx(() { Expanded(child: Obx(() {
WindowController.fromWindowId(windowId()) WindowController.fromWindowId(windowId())
.setFullscreen(_fullscreenID.value.isNotEmpty); .setFullscreen(_fullscreenID.value.isNotEmpty);
return TabBarView( return PageView(
controller: tabController.value, controller: DesktopTabBar.controller.value,
children: tabs children: tabs
.map((tab) => RemotePage( .map((tab) => RemotePage(
key: ValueKey(tab.label), key: ValueKey(tab.label),
@ -103,7 +105,6 @@ class _ConnectionTabPageState extends State<ConnectionTabPage>
} }
void onRemoveId(String id) { void onRemoveId(String id) {
DesktopTabBar.onClose(this, tabController, tabs, id);
ffi(id).close(); ffi(id).close();
if (tabs.length == 0) { if (tabs.length == 0) {
WindowController.fromWindowId(windowId()).close(); WindowController.fromWindowId(windowId()).close();

View File

@ -13,20 +13,19 @@ class DesktopTabPage extends StatefulWidget {
State<DesktopTabPage> createState() => _DesktopTabPageState(); State<DesktopTabPage> createState() => _DesktopTabPageState();
} }
class _DesktopTabPageState extends State<DesktopTabPage> class _DesktopTabPageState extends State<DesktopTabPage> {
with TickerProviderStateMixin {
late Rx<TabController> tabController;
late RxList<TabInfo> tabs; late RxList<TabInfo> tabs;
static final Rx<int> _selected = 0.obs;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
tabs = RxList.from([ tabs = RxList.from([
TabInfo(label: kTabLabelHomePage, icon: Icons.home_sharp, closable: false) TabInfo(
label: kTabLabelHomePage,
selectedIcon: Icons.home_sharp,
unselectedIcon: Icons.home_outlined,
closable: false)
], growable: true); ], growable: true);
tabController =
TabController(length: tabs.length, vsync: this, initialIndex: 0).obs;
} }
@override @override
@ -35,17 +34,14 @@ class _DesktopTabPageState extends State<DesktopTabPage>
body: Column( body: Column(
children: [ children: [
DesktopTabBar( DesktopTabBar(
controller: tabController,
tabs: tabs, tabs: tabs,
onTabClose: onTabClose,
selected: _selected,
dark: isDarkTheme(), dark: isDarkTheme(),
mainTab: true, mainTab: true,
onAddSetting: onAddSetting, onAddSetting: onAddSetting,
), ),
Obx((() => Expanded( Obx((() => Expanded(
child: TabBarView( child: PageView(
controller: tabController.value, controller: DesktopTabBar.controller.value,
children: tabs.map((tab) { children: tabs.map((tab) {
switch (tab.label) { switch (tab.label) {
case kTabLabelHomePage: case kTabLabelHomePage:
@ -62,12 +58,12 @@ class _DesktopTabPageState extends State<DesktopTabPage>
); );
} }
void onTabClose(String label) {
DesktopTabBar.onClose(this, tabController, tabs, label);
}
void onAddSetting() { void onAddSetting() {
DesktopTabBar.onAdd(this, tabController, tabs, _selected, DesktopTabBar.onAdd(
TabInfo(label: kTabLabelSettingPage, icon: Icons.build)); tabs,
TabInfo(
label: kTabLabelSettingPage,
selectedIcon: Icons.build_sharp,
unselectedIcon: Icons.build_outlined));
} }
} }

View File

@ -19,25 +19,25 @@ class FileManagerTabPage extends StatefulWidget {
State<FileManagerTabPage> createState() => _FileManagerTabPageState(params); State<FileManagerTabPage> createState() => _FileManagerTabPageState(params);
} }
class _FileManagerTabPageState extends State<FileManagerTabPage> class _FileManagerTabPageState extends State<FileManagerTabPage> {
with TickerProviderStateMixin {
// refactor List<int> when using multi-tab // refactor List<int> when using multi-tab
// this singleton is only for test // this singleton is only for test
RxList<TabInfo> tabs = List<TabInfo>.empty(growable: true).obs; RxList<TabInfo> tabs = List<TabInfo>.empty(growable: true).obs;
late Rx<TabController> tabController; final IconData selectedIcon = Icons.file_copy_sharp;
static final Rx<int> _selected = 0.obs; final IconData unselectedIcon = Icons.file_copy_outlined;
IconData icon = Icons.file_copy_sharp;
_FileManagerTabPageState(Map<String, dynamic> params) { _FileManagerTabPageState(Map<String, dynamic> params) {
if (params['id'] != null) { if (params['id'] != null) {
tabs.add(TabInfo(label: params['id'], icon: icon)); tabs.add(TabInfo(
label: params['id'],
selectedIcon: selectedIcon,
unselectedIcon: unselectedIcon));
} }
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
tabController = TabController(length: tabs.length, vsync: this).obs;
rustDeskWinManager.setMethodHandler((call, fromWindowId) async { rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
print( print(
"call ${call.method} with args ${call.arguments} from window ${fromWindowId}"); "call ${call.method} with args ${call.arguments} from window ${fromWindowId}");
@ -46,8 +46,12 @@ class _FileManagerTabPageState extends State<FileManagerTabPage>
final args = jsonDecode(call.arguments); final args = jsonDecode(call.arguments);
final id = args['id']; final id = args['id'];
window_on_top(windowId()); window_on_top(windowId());
DesktopTabBar.onAdd(this, tabController, tabs, _selected, DesktopTabBar.onAdd(
TabInfo(label: id, icon: icon)); tabs,
TabInfo(
label: id,
selectedIcon: selectedIcon,
unselectedIcon: unselectedIcon));
} else if (call.method == "onDestroy") { } else if (call.method == "onDestroy") {
print( print(
"executing onDestroy hook, closing ${tabs.map((tab) => tab.label).toList()}"); "executing onDestroy hook, closing ${tabs.map((tab) => tab.label).toList()}");
@ -68,17 +72,15 @@ class _FileManagerTabPageState extends State<FileManagerTabPage>
body: Column( body: Column(
children: [ children: [
DesktopTabBar( DesktopTabBar(
controller: tabController,
tabs: tabs, tabs: tabs,
onTabClose: onRemoveId, onTabClose: onRemoveId,
selected: _selected,
dark: isDarkTheme(), dark: isDarkTheme(),
mainTab: false, mainTab: false,
), ),
Expanded( Expanded(
child: Obx( child: Obx(
() => TabBarView( () => PageView(
controller: tabController.value, controller: DesktopTabBar.controller.value,
children: tabs children: tabs
.map((tab) => FileManagerPage( .map((tab) => FileManagerPage(
key: ValueKey(tab.label), key: ValueKey(tab.label),
@ -92,8 +94,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage>
} }
void onRemoveId(String id) { void onRemoveId(String id) {
DesktopTabBar.onClose(this, tabController, tabs, id); ffi("ft_$id").close();
ffi(id).close();
if (tabs.length == 0) { if (tabs.length == 0) {
WindowController.fromWindowId(windowId()).close(); WindowController.fromWindowId(windowId()).close();
} }

View File

@ -8,21 +8,22 @@ import 'package:flutter_hbb/main.dart';
import 'package:flutter_hbb/utils/multi_window_manager.dart'; import 'package:flutter_hbb/utils/multi_window_manager.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
import 'package:scroll_pos/scroll_pos.dart';
const double _kTabBarHeight = kDesktopRemoteTabBarHeight; const double _kTabBarHeight = kDesktopRemoteTabBarHeight;
const double _kIconSize = 18; const double _kIconSize = 18;
const double _kDividerIndent = 10; const double _kDividerIndent = 10;
const double _kAddIconSize = _kTabBarHeight - 15; const double _kAddIconSize = _kTabBarHeight - 15;
final tabBarKey = GlobalKey(); final _tabBarKey = GlobalKey();
void closeTab(String? id) { void closeTab(String? id) {
final tabBar = tabBarKey.currentWidget as TabBar?; final tabBar = _tabBarKey.currentWidget as _ListView?;
if (tabBar == null) return; if (tabBar == null) return;
final tabs = tabBar.tabs as List<_Tab>; final tabs = tabBar.tabs;
if (id == null) { if (id == null) {
final current = tabBar.controller?.index; if (tabBar.selected.value < tabs.length) {
if (current == null) return; tabs[tabBar.selected.value].onClose();
tabs[current].onClose(); }
} else { } else {
for (final tab in tabs) { for (final tab in tabs) {
if (tab.label == id) { if (tab.label == id) {
@ -35,33 +36,45 @@ void closeTab(String? id) {
class TabInfo { class TabInfo {
late final String label; late final String label;
late final IconData icon; late final IconData selectedIcon;
late final IconData unselectedIcon;
late final bool closable; late final bool closable;
TabInfo({required this.label, required this.icon, this.closable = true}); TabInfo(
{required this.label,
required this.selectedIcon,
required this.unselectedIcon,
this.closable = true});
} }
class DesktopTabBar extends StatelessWidget { class DesktopTabBar extends StatelessWidget {
late final Rx<TabController> controller;
late final RxList<TabInfo> tabs; late final RxList<TabInfo> tabs;
late final Function(String) onTabClose; late final Function(String)? onTabClose;
late final Rx<int> selected;
late final bool dark; late final bool dark;
late final _Theme _theme; late final _Theme _theme;
late final bool mainTab; late final bool mainTab;
late final Function()? onAddSetting; late final Function()? onAddSetting;
final ScrollPosController scrollController =
ScrollPosController(itemCount: 0);
static final Rx<PageController> controller = PageController().obs;
static final Rx<int> selected = 0.obs;
DesktopTabBar({ DesktopTabBar({
Key? key, Key? key,
required this.controller,
required this.tabs, required this.tabs,
required this.onTabClose, this.onTabClose,
required this.selected,
required this.dark, required this.dark,
required this.mainTab, required this.mainTab,
this.onAddSetting, this.onAddSetting,
}) : _theme = dark ? _Theme.dark() : _Theme.light(), }) : _theme = dark ? _Theme.dark() : _Theme.light(),
super(key: key); super(key: key) {
scrollController.itemCount = tabs.length;
WidgetsBinding.instance.addPostFrameCallback((_) {
debugPrint("callback");
scrollController.scrollToItem(selected.value,
center: true, animate: true);
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -89,49 +102,21 @@ class DesktopTabBar extends StatelessWidget {
.startDragging(); .startDragging();
} }
}, },
child: Obx(() => TabBar( child: _ListView(
key: tabBarKey, key: _tabBarKey,
indicatorColor: _theme.indicatorColor, controller: controller,
labelPadding: const EdgeInsets.symmetric( scrollController: scrollController,
vertical: 0, horizontal: 0), tabInfos: tabs,
isScrollable: true, selected: selected,
indicatorPadding: EdgeInsets.zero, onTabClose: onTabClose,
physics: BouncingScrollPhysics(), theme: _theme)),
controller: controller.value,
tabs: tabs.asMap().entries.map((e) {
int index = e.key;
String label = e.value.label;
return _Tab(
index: index,
label: label,
icon: e.value.icon,
closable: e.value.closable,
selected: selected.value,
onClose: () {
onTabClose(label);
if (index <= selected.value) {
selected.value = max(0, selected.value - 1);
}
controller.value.animateTo(selected.value,
duration: Duration.zero);
},
onSelected: () {
selected.value = index;
controller.value
.animateTo(index, duration: Duration.zero);
},
theme: _theme,
);
}).toList())),
),
), ),
Offstage( Offstage(
offstage: mainTab, offstage: mainTab,
child: _AddButton( child: _AddButton(
theme: _theme, theme: _theme,
).paddingOnly(left: 10), ).paddingOnly(left: 10),
) ),
], ],
), ),
), ),
@ -157,32 +142,16 @@ class DesktopTabBar extends StatelessWidget {
); );
} }
static onClose( static onAdd(RxList<TabInfo> tabs, TabInfo tab) {
TickerProvider vsync,
Rx<TabController> controller,
RxList<TabInfo> tabs,
String label,
) {
tabs.removeWhere((tab) => tab.label == label);
controller.value = TabController(
length: tabs.length,
vsync: vsync,
initialIndex: max(0, tabs.length - 1));
}
static onAdd(TickerProvider vsync, Rx<TabController> controller,
RxList<TabInfo> tabs, Rx<int> selected, TabInfo tab) {
int index = tabs.indexWhere((e) => e.label == tab.label); int index = tabs.indexWhere((e) => e.label == tab.label);
if (index >= 0) { if (index >= 0) {
controller.value.animateTo(index, duration: Duration.zero);
selected.value = index; selected.value = index;
} else { } else {
tabs.add(tab); tabs.add(tab);
controller.value = TabController(
length: tabs.length, vsync: vsync, initialIndex: tabs.length - 1);
controller.value.animateTo(tabs.length - 1, duration: Duration.zero);
selected.value = tabs.length - 1; selected.value = tabs.length - 1;
assert(selected.value >= 0);
} }
controller.value.jumpToPage(selected.value);
} }
} }
@ -265,10 +234,76 @@ class WindowActionPanel extends StatelessWidget {
} }
} }
class _ListView extends StatelessWidget {
late Rx<PageController> controller;
final ScrollPosController scrollController;
final RxList<TabInfo> tabInfos;
final Rx<int> selected;
final Function(String label)? onTabClose;
final _Theme _theme;
late List<_Tab> tabs;
_ListView({
Key? key,
required this.controller,
required this.scrollController,
required this.tabInfos,
required this.selected,
required this.onTabClose,
required _Theme theme,
}) : _theme = theme,
super(key: key);
@override
Widget build(BuildContext context) {
return Obx(() {
tabs = tabInfos.asMap().entries.map((e) {
int index = e.key;
String label = e.value.label;
return _Tab(
index: index,
label: label,
selectedIcon: e.value.selectedIcon,
unselectedIcon: e.value.unselectedIcon,
closable: e.value.closable,
selected: selected.value,
onClose: () {
tabInfos.removeWhere((tab) => tab.label == label);
onTabClose?.call(label);
if (index <= selected.value) {
selected.value = max(0, selected.value - 1);
}
assert(tabInfos.length == 0 || selected.value < tabInfos.length);
scrollController.itemCount = tabInfos.length;
if (tabInfos.length > 0) {
scrollController.scrollToItem(selected.value,
center: true, animate: true);
controller.value.jumpToPage(selected.value);
}
},
onSelected: () {
selected.value = index;
scrollController.scrollToItem(index, center: true, animate: true);
controller.value.jumpToPage(index);
},
theme: _theme,
);
}).toList();
return ListView(
controller: scrollController,
scrollDirection: Axis.horizontal,
shrinkWrap: true,
physics: BouncingScrollPhysics(),
children: tabs);
});
}
}
class _Tab extends StatelessWidget { class _Tab extends StatelessWidget {
late final int index; late final int index;
late final String label; late final String label;
late final IconData icon; late final IconData selectedIcon;
late final IconData unselectedIcon;
late final bool closable; late final bool closable;
late final int selected; late final int selected;
late final Function() onClose; late final Function() onClose;
@ -280,7 +315,8 @@ class _Tab extends StatelessWidget {
{Key? key, {Key? key,
required this.index, required this.index,
required this.label, required this.label,
required this.icon, required this.selectedIcon,
required this.unselectedIcon,
required this.closable, required this.closable,
required this.selected, required this.selected,
required this.onClose, required this.onClose,
@ -292,13 +328,16 @@ class _Tab extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool is_selected = index == selected; bool is_selected = index == selected;
bool show_divider = index != selected - 1 && index != selected; bool show_divider = index != selected - 1 && index != selected;
return Ink( return Stack(
children: [
Ink(
child: InkWell( child: InkWell(
onHover: (hover) => _hover.value = hover, onHover: (hover) => _hover.value = hover,
onTap: () => onSelected(), onTap: () => onSelected(),
child: Row( child: Row(
children: [ children: [
Tab( Container(
height: _kTabBarHeight,
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
@ -306,7 +345,7 @@ class _Tab extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon( Icon(
icon, is_selected ? selectedIcon : unselectedIcon,
size: _kIconSize, size: _kIconSize,
color: is_selected color: is_selected
? theme.selectedtabIconColor ? theme.selectedtabIconColor
@ -345,6 +384,18 @@ class _Tab extends StatelessWidget {
], ],
), ),
), ),
),
Positioned(
height: 2,
left: 0,
right: 0,
bottom: 0,
child: Center(
child: Container(
color:
is_selected ? theme.indicatorColor : Colors.transparent),
))
],
); );
} }
} }

View File

@ -49,7 +49,7 @@ packages:
name: async name: async
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.8.2" version: "2.9.0"
back_button_interceptor: back_button_interceptor:
dependency: "direct main" dependency: "direct main"
description: description:
@ -147,7 +147,7 @@ packages:
name: characters name: characters
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.2.0" version: "1.2.1"
charcode: charcode:
dependency: transitive dependency: transitive
description: description:
@ -168,7 +168,7 @@ packages:
name: clock name: clock
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.1.0" version: "1.1.1"
code_builder: code_builder:
dependency: transitive dependency: transitive
description: description:
@ -235,15 +235,17 @@ packages:
dash_chat_2: dash_chat_2:
dependency: "direct main" dependency: "direct main"
description: description:
name: dash_chat_2 path: "."
url: "https://pub.flutter-io.cn" ref: feat_maxWidth
source: hosted resolved-ref: "3946ecf86d3600b54632fd80d0eb0ef0e74f2d6a"
url: "https://github.com/fufesou/Dash-Chat-2"
source: git
version: "0.0.12" version: "0.0.12"
desktop_drop: desktop_drop:
dependency: "direct main" dependency: "direct main"
description: description:
name: desktop_drop name: desktop_drop
url: "https://pub.dartlang.org" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.3.3" version: "0.3.3"
desktop_multi_window: desktop_multi_window:
@ -324,7 +326,7 @@ packages:
name: fake_async name: fake_async
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.3.0" version: "1.3.1"
ffi: ffi:
dependency: "direct main" dependency: "direct main"
description: description:
@ -607,14 +609,14 @@ packages:
name: matcher name: matcher
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.12.11" version: "0.12.12"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
name: material_color_utilities name: material_color_utilities
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.1.4" version: "0.1.5"
menu_base: menu_base:
dependency: transitive dependency: transitive
description: description:
@ -628,7 +630,7 @@ packages:
name: meta name: meta
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.7.0" version: "1.8.0"
mime: mime:
dependency: transitive dependency: transitive
description: description:
@ -705,7 +707,7 @@ packages:
name: path name: path
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.8.1" version: "1.8.2"
path_provider: path_provider:
dependency: "direct main" dependency: "direct main"
description: description:
@ -846,6 +848,13 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.1.2" version: "0.1.2"
scroll_pos:
dependency: "direct main"
description:
name: scroll_pos
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.0"
settings_ui: settings_ui:
dependency: "direct main" dependency: "direct main"
description: description:
@ -948,7 +957,7 @@ packages:
name: source_span name: source_span
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.8.2" version: "1.9.0"
sqflite: sqflite:
dependency: transitive dependency: transitive
description: description:
@ -990,7 +999,7 @@ packages:
name: string_scanner name: string_scanner
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.1.0" version: "1.1.1"
synchronized: synchronized:
dependency: transitive dependency: transitive
description: description:
@ -1004,14 +1013,14 @@ packages:
name: term_glyph name: term_glyph
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.2.0" version: "1.2.1"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.4.9" version: "0.4.12"
timing: timing:
dependency: transitive dependency: transitive
description: description:
@ -1029,10 +1038,12 @@ packages:
tray_manager: tray_manager:
dependency: "direct main" dependency: "direct main"
description: description:
name: tray_manager path: "."
url: "https://pub.flutter-io.cn" ref: "3aa37c86e47ea748e7b5507cbe59f2c54ebdb23a"
source: hosted resolved-ref: "3aa37c86e47ea748e7b5507cbe59f2c54ebdb23a"
version: "0.1.7" url: "https://github.com/Kingtous/rustdesk_tray_manager"
source: git
version: "0.1.8"
tuple: tuple:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -76,6 +76,7 @@ dependencies:
visibility_detector: ^0.3.3 visibility_detector: ^0.3.3
contextmenu: ^3.0.0 contextmenu: ^3.0.0
desktop_drop: ^0.3.3 desktop_drop: ^0.3.3
scroll_pos: ^0.3.0
dev_dependencies: dev_dependencies:
flutter_launcher_icons: ^0.9.1 flutter_launcher_icons: ^0.9.1