Merge pull request #1740 from Heap-Hop/master

update file transfer and chat page
This commit is contained in:
RustDesk 2022-10-18 08:53:08 +08:00 committed by GitHub
commit b8c9113ee5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 335 additions and 259 deletions

View File

@ -36,7 +36,7 @@ class DraggableChatWindow extends StatelessWidget {
appBar: CustomAppBar( appBar: CustomAppBar(
onPanUpdate: onPanUpdate, onPanUpdate: onPanUpdate,
appBar: isDesktop appBar: isDesktop
? _buildDesktopAppBar() ? _buildDesktopAppBar(context)
: _buildMobileAppBar(context), : _buildMobileAppBar(context),
), ),
body: ChatPage(chatModel: chatModel), body: ChatPage(chatModel: chatModel),
@ -82,33 +82,33 @@ class DraggableChatWindow extends StatelessWidget {
); );
} }
Widget _buildDesktopAppBar() { Widget _buildDesktopAppBar(BuildContext context) {
return Container( return Container(
color: MyTheme.accent50, decoration: BoxDecoration(
height: 35, border: Border(
bottom: BorderSide(
color: Theme.of(context).hintColor.withOpacity(0.4)))),
height: 38,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 15), padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8),
child: Text( child: Row(children: [
translate("Chat"), Icon(Icons.chat_bubble_outline,
style: const TextStyle( size: 20, color: Theme.of(context).colorScheme.primary),
color: Colors.white, SizedBox(width: 6),
fontFamily: 'WorkSans', Text(translate("Chat"))
fontWeight: FontWeight.bold), ])),
)), Padding(
Row( padding: EdgeInsets.all(2),
crossAxisAlignment: CrossAxisAlignment.center, child: ActionIcon(
children: [
ActionIcon(
message: 'Close', message: 'Close',
icon: IconFont.close, icon: IconFont.close,
onTap: chatModel.hideChatWindowOverlay, onTap: chatModel.hideChatWindowOverlay,
isClose: true, isClose: true,
) size: 32,
], ))
)
], ],
), ),
); );

View File

@ -31,6 +31,8 @@ const int kMobileMaxDisplayHeight = 1280;
const int kDesktopMaxDisplayWidth = 1920; const int kDesktopMaxDisplayWidth = 1920;
const int kDesktopMaxDisplayHeight = 1080; const int kDesktopMaxDisplayHeight = 1080;
const int kDesktopDoubleClickTimeMilli = 200;
const Size kConnectionManagerWindowSize = Size(300, 400); const Size kConnectionManagerWindowSize = Size(300, 400);
// Tabbar transition duration, now we remove the duration // Tabbar transition duration, now we remove the duration
const Duration kTabTransitionDuration = Duration.zero; const Duration kTabTransitionDuration = Duration.zero;

View File

@ -1,10 +1,11 @@
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:desktop_drop/desktop_drop.dart'; import 'package:desktop_drop/desktop_drop.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_breadcrumb/flutter_breadcrumb.dart'; import 'package:flutter_breadcrumb/flutter_breadcrumb.dart';
import 'package:flutter_hbb/mobile/pages/file_manager_page.dart';
import 'package:flutter_hbb/models/file_model.dart'; import 'package:flutter_hbb/models/file_model.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -44,15 +45,17 @@ class _FileManagerPageState extends State<FileManagerPage>
final _locationStatusLocal = LocationStatus.bread.obs; final _locationStatusLocal = LocationStatus.bread.obs;
final _locationStatusRemote = LocationStatus.bread.obs; final _locationStatusRemote = LocationStatus.bread.obs;
final FocusNode _locationNodeLocal = final _locationNodeLocal = FocusNode(debugLabel: "locationNodeLocal");
FocusNode(debugLabel: "locationNodeLocal"); final _locationNodeRemote = FocusNode(debugLabel: "locationNodeRemote");
final FocusNode _locationNodeRemote =
FocusNode(debugLabel: "locationNodeRemote");
final _searchTextLocal = "".obs; final _searchTextLocal = "".obs;
final _searchTextRemote = "".obs; final _searchTextRemote = "".obs;
final _breadCrumbScrollerLocal = ScrollController(); final _breadCrumbScrollerLocal = ScrollController();
final _breadCrumbScrollerRemote = ScrollController(); final _breadCrumbScrollerRemote = ScrollController();
/// [_lastClickTime], [_lastClickEntry] help to handle double click
int _lastClickTime = DateTime.now().millisecondsSinceEpoch;
Entry? _lastClickEntry;
final _dropMaskVisible = false.obs; // TODO impl drop mask final _dropMaskVisible = false.obs; // TODO impl drop mask
ScrollController getBreadCrumbScrollController(bool isLocal) { ScrollController getBreadCrumbScrollController(bool isLocal) {
@ -171,22 +174,6 @@ class _FileManagerPageState extends State<FileManagerPage>
} }
Widget body({bool isLocal = false}) { Widget body({bool isLocal = false}) {
final fd = model.getCurrentDir(isLocal);
final entries = fd.entries;
final sortIndex = (SortBy style) {
switch (style) {
case SortBy.Name:
return 0;
case SortBy.Type:
return 0;
case SortBy.Modified:
return 1;
case SortBy.Size:
return 2;
}
}(model.getSortStyle(isLocal));
final sortAscending =
isLocal ? model.localSortAscending : model.remoteSortAscending;
return Container( return Container(
decoration: BoxDecoration(border: Border.all(color: Colors.black26)), decoration: BoxDecoration(border: Border.all(color: Colors.black26)),
margin: const EdgeInsets.all(16.0), margin: const EdgeInsets.all(16.0),
@ -208,7 +195,35 @@ class _FileManagerPageState extends State<FileManagerPage>
Expanded( Expanded(
child: SingleChildScrollView( child: SingleChildScrollView(
controller: ScrollController(), controller: ScrollController(),
child: ObxValue<RxString>( child: _buildDataTable(context, isLocal),
),
)
],
)),
]),
),
);
}
Widget _buildDataTable(BuildContext context, bool isLocal) {
final fd = model.getCurrentDir(isLocal);
final entries = fd.entries;
final sortIndex = (SortBy style) {
switch (style) {
case SortBy.Name:
return 0;
case SortBy.Type:
return 0;
case SortBy.Modified:
return 1;
case SortBy.Size:
return 2;
}
}(model.getSortStyle(isLocal));
final sortAscending =
isLocal ? model.localSortAscending : model.remoteSortAscending;
return ObxValue<RxString>(
(searchText) { (searchText) {
final filteredEntries = searchText.isNotEmpty final filteredEntries = searchText.isNotEmpty
? entries.where((element) { ? entries.where((element) {
@ -217,7 +232,7 @@ class _FileManagerPageState extends State<FileManagerPage>
: entries; : entries;
return DataTable( return DataTable(
key: ValueKey(isLocal ? 0 : 1), key: ValueKey(isLocal ? 0 : 1),
showCheckboxColumn: true, showCheckboxColumn: false,
dataRowHeight: 25, dataRowHeight: 25,
headingRowHeight: 30, headingRowHeight: 30,
horizontalMargin: 8, horizontalMargin: 8,
@ -250,35 +265,28 @@ class _FileManagerPageState extends State<FileManagerPage>
}), }),
], ],
rows: filteredEntries.map((entry) { rows: filteredEntries.map((entry) {
final sizeStr = entry.isFile final sizeStr =
? readableFileSize(entry.size.toDouble()) entry.isFile ? readableFileSize(entry.size.toDouble()) : "";
: ""; final lastModifiedStr =
"${entry.lastModified().toString().replaceAll(".000", "")} ";
return DataRow( return DataRow(
key: ValueKey(entry.name), key: ValueKey(entry.name),
onSelectChanged: (s) { onSelectChanged: (s) {
if (s != null) { _onSelectedChanged(getSelectedItem(isLocal), filteredEntries,
if (s) { entry, isLocal);
getSelectedItem(isLocal)
.add(isLocal, entry);
} else {
getSelectedItem(isLocal).remove(entry);
}
setState(() {}); setState(() {});
}
}, },
selected: selected: getSelectedItem(isLocal).contains(entry),
getSelectedItem(isLocal).contains(entry),
cells: [ cells: [
DataCell( DataCell(
Container( Container(
width: 180, width: 200,
child: Tooltip( child: Tooltip(
waitDuration: Duration(milliseconds: 500),
message: entry.name, message: entry.name,
child: Row(children: [ child: Row(children: [
Icon( Icon(
entry.isFile entry.isFile ? Icons.feed_outlined : Icons.folder,
? Icons.feed_outlined
: Icons.folder,
size: 20, size: 20,
color: Theme.of(context) color: Theme.of(context)
.iconTheme .iconTheme
@ -287,56 +295,93 @@ class _FileManagerPageState extends State<FileManagerPage>
).marginSymmetric(horizontal: 2), ).marginSymmetric(horizontal: 2),
Expanded( Expanded(
child: Text(entry.name, child: Text(entry.name,
overflow: overflow: TextOverflow.ellipsis))
TextOverflow.ellipsis))
]), ]),
)), onTap: () { )),
if (entry.isDirectory) { onTap: () {
final items = getSelectedItem(isLocal);
// handle double click
if (_checkDoubleClick(entry)) {
openDirectory(entry.path, isLocal: isLocal); openDirectory(entry.path, isLocal: isLocal);
if (isLocal) { items.clear();
_localSelectedItems.clear(); return;
} else {
_remoteSelectedItems.clear();
} }
} else { _onSelectedChanged(
// Perform file-related tasks. items, filteredEntries, entry, isLocal);
final selectedItems = },
getSelectedItem(isLocal); ),
if (selectedItems.contains(entry)) {
selectedItems.remove(entry);
} else {
selectedItems.add(isLocal, entry);
}
setState(() {});
}
}),
DataCell(FittedBox( DataCell(FittedBox(
child: Tooltip(
waitDuration: Duration(milliseconds: 500),
message: lastModifiedStr,
child: Text( child: Text(
"${entry.lastModified().toString().replaceAll(".000", "")} ", lastModifiedStr,
style: TextStyle( style: TextStyle(
fontSize: 12, color: MyTheme.darkGray), fontSize: 12, color: MyTheme.darkGray),
))), )))),
DataCell(Text( DataCell(Tooltip(
waitDuration: Duration(milliseconds: 500),
message: sizeStr,
child: Text(
sizeStr, sizeStr,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle( style: TextStyle(fontSize: 10, color: MyTheme.darkGray),
fontSize: 10, color: MyTheme.darkGray), ))),
)),
]); ]);
}).toList(growable: false), }).toList(growable: false),
); );
}, },
isLocal ? _searchTextLocal : _searchTextRemote, isLocal ? _searchTextLocal : _searchTextRemote,
),
),
)
],
)),
]),
),
); );
} }
void _onSelectedChanged(SelectedItems selectedItems, List<Entry> entries,
Entry entry, bool isLocal) {
final isCtrlDown = RawKeyboard.instance.keysPressed
.contains(LogicalKeyboardKey.controlLeft);
final isShiftDown =
RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.shiftLeft);
if (isCtrlDown) {
if (selectedItems.contains(entry)) {
selectedItems.remove(entry);
} else {
selectedItems.add(isLocal, entry);
}
} else if (isShiftDown) {
final List<int> indexGroup = [];
for (var selected in selectedItems.items) {
indexGroup.add(entries.indexOf(selected));
}
indexGroup.add(entries.indexOf(entry));
indexGroup.removeWhere((e) => e == -1);
final maxIndex = indexGroup.reduce(max);
final minIndex = indexGroup.reduce(min);
selectedItems.clear();
entries
.getRange(minIndex, maxIndex + 1)
.forEach((e) => selectedItems.add(isLocal, e));
} else {
selectedItems.clear();
selectedItems.add(isLocal, entry);
}
setState(() {});
}
bool _checkDoubleClick(Entry entry) {
final current = DateTime.now().millisecondsSinceEpoch;
final elapsed = current - _lastClickTime;
_lastClickTime = current;
if (_lastClickEntry == entry) {
if (elapsed < kDesktopDoubleClickTimeMilli) {
return true;
}
} else {
_lastClickEntry = entry;
}
return false;
}
/// transfer status list /// transfer status list
/// watch transfer status /// watch transfer status
Widget statusList() { Widget statusList() {
@ -369,6 +414,7 @@ class _FileManagerPageState extends State<FileManagerPage>
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Tooltip( Tooltip(
waitDuration: Duration(milliseconds: 500),
message: item.jobName, message: item.jobName,
child: Text( child: Text(
item.jobName, item.jobName,
@ -787,8 +833,10 @@ class _FileManagerPageState extends State<FileManagerPage>
onSearchText(String searchText, bool isLocal) { onSearchText(String searchText, bool isLocal) {
if (isLocal) { if (isLocal) {
_localSelectedItems.clear();
_searchTextLocal.value = searchText; _searchTextLocal.value = searchText;
} else { } else {
_remoteSelectedItems.clear();
_searchTextRemote.value = searchText; _searchTextRemote.value = searchText;
} }
} }

View File

@ -191,6 +191,8 @@ class DesktopTab extends StatelessWidget {
final DesktopTabController controller; final DesktopTabController controller;
Rx<DesktopTabState> get state => controller.state; Rx<DesktopTabState> get state => controller.state;
final isMaximized = false.obs;
late final DesktopTabType tabType; late final DesktopTabType tabType;
late final bool isMainWindow; late final bool isMainWindow;
@ -299,8 +301,10 @@ class DesktopTab extends StatelessWidget {
width: 78, width: 78,
)), )),
GestureDetector( GestureDetector(
onDoubleTap: () => onDoubleTap: showMaximize
showMaximize ? toggleMaximize(isMainWindow) : null, ? () => toggleMaximize(isMainWindow)
.then((value) => isMaximized.value = value)
: null,
onPanStart: (_) => startDragging(isMainWindow), onPanStart: (_) => startDragging(isMainWindow),
child: Row(children: [ child: Row(children: [
Offstage( Offstage(
@ -333,6 +337,7 @@ class DesktopTab extends StatelessWidget {
tabType: tabType, tabType: tabType,
state: state, state: state,
tail: tail, tail: tail,
isMaximized: isMaximized,
showMinimize: showMinimize, showMinimize: showMinimize,
showMaximize: showMaximize, showMaximize: showMaximize,
showClose: showClose, showClose: showClose,
@ -347,6 +352,7 @@ class WindowActionPanel extends StatefulWidget {
final bool isMainWindow; final bool isMainWindow;
final DesktopTabType tabType; final DesktopTabType tabType;
final Rx<DesktopTabState> state; final Rx<DesktopTabState> state;
final RxBool isMaximized;
final bool showMinimize; final bool showMinimize;
final bool showMaximize; final bool showMaximize;
@ -359,6 +365,7 @@ class WindowActionPanel extends StatefulWidget {
required this.isMainWindow, required this.isMainWindow,
required this.tabType, required this.tabType,
required this.state, required this.state,
required this.isMaximized,
this.tail, this.tail,
this.showMinimize = true, this.showMinimize = true,
this.showMaximize = true, this.showMaximize = true,
@ -374,30 +381,31 @@ class WindowActionPanel extends StatefulWidget {
class WindowActionPanelState extends State<WindowActionPanel> class WindowActionPanelState extends State<WindowActionPanel>
with MultiWindowListener, WindowListener { with MultiWindowListener, WindowListener {
bool isMaximized = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
DesktopMultiWindow.addListener(this); DesktopMultiWindow.addListener(this);
windowManager.addListener(this); windowManager.addListener(this);
Future.delayed(Duration(milliseconds: 500), () {
if (widget.isMainWindow) { if (widget.isMainWindow) {
windowManager.isMaximized().then((maximized) { windowManager.isMaximized().then((maximized) {
if (isMaximized != maximized) { if (widget.isMaximized.value != maximized) {
WidgetsBinding.instance.addPostFrameCallback( WidgetsBinding.instance.addPostFrameCallback(
(_) => setState(() => isMaximized = maximized)); (_) => setState(() => widget.isMaximized.value = maximized));
} }
}); });
} else { } else {
final wc = WindowController.fromWindowId(windowId!); final wc = WindowController.fromWindowId(windowId!);
wc.isMaximized().then((maximized) { wc.isMaximized().then((maximized) {
if (isMaximized != maximized) { debugPrint("isMaximized $maximized");
if (widget.isMaximized.value != maximized) {
WidgetsBinding.instance.addPostFrameCallback( WidgetsBinding.instance.addPostFrameCallback(
(_) => setState(() => isMaximized = maximized)); (_) => setState(() => widget.isMaximized.value = maximized));
} }
}); });
} }
});
} }
@override @override
@ -410,8 +418,8 @@ class WindowActionPanelState extends State<WindowActionPanel>
@override @override
void onWindowMaximize() { void onWindowMaximize() {
// catch maximize from system // catch maximize from system
if (!isMaximized) { if (!widget.isMaximized.value) {
setState(() => isMaximized = true); widget.isMaximized.value = true;
} }
super.onWindowMaximize(); super.onWindowMaximize();
} }
@ -419,8 +427,8 @@ class WindowActionPanelState extends State<WindowActionPanel>
@override @override
void onWindowUnmaximize() { void onWindowUnmaximize() {
// catch unmaximize from system // catch unmaximize from system
if (isMaximized) { if (widget.isMaximized.value) {
setState(() => isMaximized = false); widget.isMaximized.value = false;
} }
super.onWindowUnmaximize(); super.onWindowUnmaximize();
} }
@ -452,12 +460,14 @@ class WindowActionPanelState extends State<WindowActionPanel>
)), )),
Offstage( Offstage(
offstage: !widget.showMaximize, offstage: !widget.showMaximize,
child: ActionIcon( child: Obx(() => ActionIcon(
message: isMaximized ? "Restore" : "Maximize", message: widget.isMaximized.value ? "Restore" : "Maximize",
icon: isMaximized ? IconFont.restore : IconFont.max, icon: widget.isMaximized.value
? IconFont.restore
: IconFont.max,
onTap: _toggleMaximize, onTap: _toggleMaximize,
isClose: false, isClose: false,
)), ))),
Offstage( Offstage(
offstage: !widget.showClose, offstage: !widget.showClose,
child: ActionIcon( child: ActionIcon(
@ -484,9 +494,9 @@ class WindowActionPanelState extends State<WindowActionPanel>
void _toggleMaximize() { void _toggleMaximize() {
toggleMaximize(widget.isMainWindow).then((maximize) { toggleMaximize(widget.isMainWindow).then((maximize) {
if (isMaximized != maximize) { if (widget.isMaximized.value != maximize) {
// setState for sub window, wc.unmaximize/maximize() will not invoke onWindowMaximize/Unmaximize // update state for sub window, wc.unmaximize/maximize() will not invoke onWindowMaximize/Unmaximize
setState(() => isMaximized = !isMaximized); widget.isMaximized.value = maximize;
} }
}); });
} }
@ -801,13 +811,15 @@ class ActionIcon extends StatelessWidget {
final IconData icon; final IconData icon;
final Function() onTap; final Function() onTap;
final bool isClose; final bool isClose;
const ActionIcon({ final double? size;
Key? key, const ActionIcon(
{Key? key,
required this.message, required this.message,
required this.icon, required this.icon,
required this.onTap, required this.onTap,
required this.isClose, required this.isClose,
}) : super(key: key); this.size})
: super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -822,8 +834,8 @@ class ActionIcon extends StatelessWidget {
onHover: (value) => hover.value = value, onHover: (value) => hover.value = value,
onTap: onTap, onTap: onTap,
child: SizedBox( child: SizedBox(
height: _kTabBarHeight - 1, height: size ?? (_kTabBarHeight - 1),
width: _kTabBarHeight - 1, width: size ?? (_kTabBarHeight - 1),
child: Icon( child: Icon(
icon, icon,
color: hover.value && isClose color: hover.value && isClose

View File

@ -61,19 +61,36 @@ class ChatPage extends StatelessWidget implements PageShape {
[], [],
inputOptions: InputOptions( inputOptions: InputOptions(
sendOnEnter: true, sendOnEnter: true,
inputDecoration: defaultInputDecoration(
hintText: "${translate('Write a message')}...",
fillColor: Theme.of(context).backgroundColor),
sendButtonBuilder: defaultSendButton(
color: Theme.of(context)
.textTheme
.titleLarge!
.color!),
inputTextStyle: TextStyle( inputTextStyle: TextStyle(
fontSize: 14,
color: Theme.of(context) color: Theme.of(context)
.textTheme .textTheme
.titleLarge .titleLarge
?.color)), ?.color),
inputDecoration: isDesktop
? InputDecoration(
isDense: true,
hintText:
"${translate('Write a message')}...",
filled: true,
fillColor: Theme.of(context).backgroundColor,
contentPadding: EdgeInsets.all(10),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: const BorderSide(
width: 0,
style: BorderStyle.none,
),
),
)
: defaultInputDecoration(
hintText:
"${translate('Write a message')}...",
fillColor: Theme.of(context).backgroundColor),
sendButtonBuilder: defaultSendButton(
padding: EdgeInsets.symmetric(
horizontal: 6, vertical: 0),
color: Theme.of(context).colorScheme.primary)),
messageOptions: MessageOptions( messageOptions: MessageOptions(
showOtherUsersAvatar: false, showOtherUsersAvatar: false,
showTime: true, showTime: true,

View File

@ -555,50 +555,3 @@ class BottomSheetBody extends StatelessWidget {
); );
} }
} }
class SelectedItems {
bool? _isLocal;
final List<Entry> _items = [];
List<Entry> get items => _items;
int get length => _items.length;
bool? get isLocal => _isLocal;
add(bool isLocal, Entry e) {
if (_isLocal == null) {
_isLocal = isLocal;
}
if (_isLocal != null && _isLocal != isLocal) {
return;
}
if (!_items.contains(e)) {
_items.add(e);
}
}
bool contains(Entry e) {
return _items.contains(e);
}
remove(Entry e) {
_items.remove(e);
if (_items.length == 0) {
_isLocal = null;
}
}
bool isOtherPage(bool currentIsLocal) {
if (_isLocal == null) {
return false;
} else {
return _isLocal != currentIsLocal;
}
}
clear() {
_items.clear();
_isLocal = null;
}
}

View File

@ -3,7 +3,6 @@ import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/mobile/pages/file_manager_page.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:path/path.dart' as Path; import 'package:path/path.dart' as Path;
@ -1123,6 +1122,51 @@ class DirectoryOption {
} }
} }
class SelectedItems {
bool? _isLocal;
final List<Entry> _items = [];
List<Entry> get items => _items;
int get length => _items.length;
bool? get isLocal => _isLocal;
add(bool isLocal, Entry e) {
_isLocal ??= isLocal;
if (_isLocal != null && _isLocal != isLocal) {
return;
}
if (!_items.contains(e)) {
_items.add(e);
}
}
bool contains(Entry e) {
return _items.contains(e);
}
remove(Entry e) {
_items.remove(e);
if (_items.length == 0) {
_isLocal = null;
}
}
bool isOtherPage(bool currentIsLocal) {
if (_isLocal == null) {
return false;
} else {
return _isLocal != currentIsLocal;
}
}
clear() {
_items.clear();
_isLocal = null;
}
}
// code from file_manager pkg after edit // code from file_manager pkg after edit
List<Entry> _sortList(List<Entry> list, SortBy sortType, bool ascending) { List<Entry> _sortList(List<Entry> list, SortBy sortType, bool ascending) {
if (sortType == SortBy.Name) { if (sortType == SortBy.Name) {