diff --git a/flutter/assets/arrow.svg b/flutter/assets/arrow.svg new file mode 100644 index 000000000..d0f032bc2 --- /dev/null +++ b/flutter/assets/arrow.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/flutter/assets/dots.svg b/flutter/assets/dots.svg new file mode 100644 index 000000000..19563b849 --- /dev/null +++ b/flutter/assets/dots.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/flutter/assets/file.svg b/flutter/assets/file.svg new file mode 100644 index 000000000..21c7fb9de --- /dev/null +++ b/flutter/assets/file.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/flutter/assets/folder.svg b/flutter/assets/folder.svg new file mode 100644 index 000000000..3959f7874 --- /dev/null +++ b/flutter/assets/folder.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/flutter/assets/folder_new.svg b/flutter/assets/folder_new.svg new file mode 100644 index 000000000..22b729204 --- /dev/null +++ b/flutter/assets/folder_new.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/flutter/assets/home.svg b/flutter/assets/home.svg new file mode 100644 index 000000000..45a018f5d --- /dev/null +++ b/flutter/assets/home.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/flutter/assets/refresh.svg b/flutter/assets/refresh.svg new file mode 100644 index 000000000..f77fcfd4c --- /dev/null +++ b/flutter/assets/refresh.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/flutter/assets/search.svg b/flutter/assets/search.svg new file mode 100644 index 000000000..295136d7e --- /dev/null +++ b/flutter/assets/search.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/flutter/assets/trash.svg b/flutter/assets/trash.svg new file mode 100644 index 000000000..f9037e0e1 --- /dev/null +++ b/flutter/assets/trash.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index e1dd1a1f8..ff8dfbb09 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -152,7 +152,7 @@ class MyTheme { static const Color canvasColor = Color(0xFF212121); static const Color border = Color(0xFFCCCCCC); static const Color idColor = Color(0xFF00B6F0); - static const Color darkGray = Color(0xFFB9BABC); + static const Color darkGray = Color.fromARGB(255, 148, 148, 148); static const Color cmIdColor = Color(0xFF21790B); static const Color dark = Colors.black87; static const Color button = Color(0xFF2C8CFF); @@ -160,8 +160,9 @@ class MyTheme { static ThemeData lightTheme = ThemeData( brightness: Brightness.light, - backgroundColor: Color(0xFFFFFFFF), - scaffoldBackgroundColor: Color(0xFFEEEEEE), + backgroundColor: Color(0xFFEEEEEE), + hoverColor: Color.fromARGB(255, 224, 224, 224), + scaffoldBackgroundColor: Color(0xFFFFFFFF), textTheme: const TextTheme( titleLarge: TextStyle(fontSize: 19, color: Colors.black87), titleSmall: TextStyle(fontSize: 14, color: Colors.black87), @@ -169,6 +170,7 @@ class MyTheme { bodyMedium: TextStyle(fontSize: 14, color: Colors.black87, height: 1.25), labelLarge: TextStyle(fontSize: 16.0, color: MyTheme.accent80)), + cardColor: Color(0xFFEEEEEE), hintColor: Color(0xFFAAAAAA), primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, @@ -191,8 +193,9 @@ class MyTheme { ); static ThemeData darkTheme = ThemeData( brightness: Brightness.dark, - backgroundColor: Color(0xFF252525), - scaffoldBackgroundColor: Color(0xFF141414), + backgroundColor: Color(0xFF24252B), + hoverColor: Color.fromARGB(255, 45, 46, 53), + scaffoldBackgroundColor: Color(0xFF18191E), textTheme: const TextTheme( titleLarge: TextStyle(fontSize: 19), titleSmall: TextStyle(fontSize: 14), @@ -200,7 +203,7 @@ class MyTheme { bodyMedium: TextStyle(fontSize: 14, height: 1.25), labelLarge: TextStyle( fontSize: 16.0, fontWeight: FontWeight.bold, color: accent80)), - cardColor: Color(0xFF252525), + cardColor: Color(0xFF24252B), primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, tabBarTheme: const TabBarTheme( @@ -217,9 +220,8 @@ class MyTheme { style: ButtonStyle(splashFactory: NoSplash.splashFactory), ) : null, - checkboxTheme: const CheckboxThemeData( - checkColor: MaterialStatePropertyAll(dark) - ), + checkboxTheme: + const CheckboxThemeData(checkColor: MaterialStatePropertyAll(dark)), ).copyWith( extensions: >[ ColorThemeExtension.dark, diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index a4cb50025..2b73182fd 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -53,7 +53,7 @@ const int kDesktopMaxDisplayHeight = 1080; const double kDesktopFileTransferNameColWidth = 200; const double kDesktopFileTransferModifiedColWidth = 120; -const double kDesktopFileTransferRowHeight = 25.0; +const double kDesktopFileTransferRowHeight = 30.0; const double kDesktopFileTransferHeaderHeight = 25.0; // https://en.wikipedia.org/wiki/Non-breaking_space diff --git a/flutter/lib/desktop/pages/file_manager_page.dart b/flutter/lib/desktop/pages/file_manager_page.dart index 4edffb3b6..0d55552af 100644 --- a/flutter/lib/desktop/pages/file_manager_page.dart +++ b/flutter/lib/desktop/pages/file_manager_page.dart @@ -2,20 +2,23 @@ import 'dart:async'; import 'dart:io'; import 'dart:math'; +import 'package:percent_indicator/percent_indicator.dart'; import 'package:desktop_drop/desktop_drop.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_breadcrumb/flutter_breadcrumb.dart'; import 'package:flutter_hbb/desktop/widgets/list_search_action_listener.dart'; +import 'package:flutter_hbb/desktop/widgets/menu_button.dart'; import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart'; import 'package:flutter_hbb/models/file_model.dart'; +import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; import 'package:wakelock/wakelock.dart'; + import '../../consts.dart'; import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu; - import '../../common.dart'; import '../../models/model.dart'; import '../../models/platform_model.dart'; @@ -147,7 +150,7 @@ class _FileManagerPageState extends State value: _ffi.fileModel, child: Consumer(builder: (context, model, child) { return Scaffold( - backgroundColor: Theme.of(context).backgroundColor, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, body: Row( children: [ Flexible(flex: 3, child: body(isLocal: true)), @@ -192,35 +195,42 @@ class _FileManagerPageState extends State ]; return Listener( - onPointerDown: (e) { - final x = e.position.dx; - final y = e.position.dy; - menuPos = RelativeRect.fromLTRB(x, y, x, y); - }, - child: IconButton( - icon: const Icon(Icons.more_vert), - splashRadius: kDesktopIconButtonSplashRadius, - onPressed: () => mod_menu.showMenu( - context: context, - position: menuPos, - items: items - .map((e) => e.build( - context, - MenuConfig( - commonColor: CustomPopupMenuTheme.commonColor, - height: CustomPopupMenuTheme.height, - dividerHeight: CustomPopupMenuTheme.dividerHeight))) - .expand((i) => i) - .toList(), - elevation: 8, - ), - )); + onPointerDown: (e) { + final x = e.position.dx; + final y = e.position.dy; + menuPos = RelativeRect.fromLTRB(x, y, x, y); + }, + child: MenuButton( + onPressed: () => mod_menu.showMenu( + context: context, + position: menuPos, + items: items + .map( + (e) => e.build( + context, + MenuConfig( + commonColor: CustomPopupMenuTheme.commonColor, + height: CustomPopupMenuTheme.height, + dividerHeight: CustomPopupMenuTheme.dividerHeight), + ), + ) + .expand((i) => i) + .toList(), + elevation: 8, + ), + child: SvgPicture.asset( + "assets/dots.svg", + color: Theme.of(context).tabBarTheme.labelColor, + ), + color: Theme.of(context).cardColor, + hoverColor: Theme.of(context).hoverColor, + ), + ); } Widget body({bool isLocal = false}) { final scrollController = ScrollController(); return Container( - decoration: BoxDecoration(border: Border.all(color: Colors.black26)), margin: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(8.0), child: DropTarget( @@ -231,18 +241,22 @@ class _FileManagerPageState extends State onDragExited: (exit) { _dropMaskVisible.value = false; }, - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - headTools(isLocal), - Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + headTools(isLocal), + Expanded( child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: _buildFileList(context, isLocal, scrollController), - ) - ], - )), - ]), + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: _buildFileList(context, isLocal, scrollController), + ) + ], + ), + ), + ], + ), ), ); } @@ -295,8 +309,7 @@ class _FileManagerPageState extends State }); return; } - _jumpToEntry( - isLocal, searchResult.first, scrollController, + _jumpToEntry(isLocal, searchResult.first, scrollController, kDesktopFileTransferRowHeight, buffer); }, onSearch: (buffer) { @@ -311,8 +324,7 @@ class _FileManagerPageState extends State }); return; } - _jumpToEntry( - isLocal, searchResult.first, scrollController, + _jumpToEntry(isLocal, searchResult.first, scrollController, kDesktopFileTransferRowHeight, buffer); }, child: ObxValue( @@ -323,100 +335,115 @@ class _FileManagerPageState extends State }).toList(growable: false) : entries; final rows = filteredEntries.map((entry) { - final sizeStr = - entry.isFile ? readableFileSize(entry.size.toDouble()) : ""; - final lastModifiedStr = entry.isDrive - ? " " - : "${entry.lastModified().toString().replaceAll(".000", "")} "; + final sizeStr = + entry.isFile ? readableFileSize(entry.size.toDouble()) : ""; + final lastModifiedStr = entry.isDrive + ? " " + : "${entry.lastModified().toString().replaceAll(".000", "")} "; final isSelected = selectedEntries.contains(entry); - return SizedBox( - key: ValueKey(entry.name), - height: kDesktopFileTransferRowHeight, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - const Divider( - height: 1, - ), - Expanded( - child: Ink( - decoration: isSelected - ? BoxDecoration(color: Theme.of(context).hoverColor) - : null, - child: InkWell( - child: Row(children: [ - GestureDetector( - child: Container( - width: kDesktopFileTransferNameColWidth, - child: Tooltip( - waitDuration: Duration(milliseconds: 500), - message: entry.name, - child: Row(children: [ - entry.isDrive - ? Image( - image: iconHardDrive, - fit: BoxFit.scaleDown, - color: Theme.of(context) - .iconTheme - .color - ?.withOpacity(0.7)) - .paddingAll(4) - : Icon( - entry.isFile - ? Icons.feed_outlined - : Icons.folder, - size: 20, - color: Theme.of(context) - .iconTheme - .color - ?.withOpacity(0.7), - ).marginSymmetric(horizontal: 2), - Expanded( - child: Text(entry.name.nonBreaking, - overflow: TextOverflow.ellipsis)) - ]), - )), - onTap: () { - final items = getSelectedItems(isLocal); - // handle double click - if (_checkDoubleClick(entry)) { - openDirectory(entry.path, isLocal: isLocal); - items.clear(); - return; - } - _onSelectedChanged( - items, filteredEntries, entry, isLocal); - }, - ), - GestureDetector( - child: SizedBox( - width: kDesktopFileTransferModifiedColWidth, - child: Tooltip( - waitDuration: Duration(milliseconds: 500), - message: lastModifiedStr, - child: Text( - lastModifiedStr, - style: TextStyle( - fontSize: 12, color: MyTheme.darkGray), - )), - )), - GestureDetector( - child: Tooltip( - waitDuration: Duration(milliseconds: 500), - message: sizeStr, - child: Text( - sizeStr, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 10, - color: MyTheme.darkGray), - ))), - ]), - ), + return Padding( + padding: EdgeInsets.symmetric(vertical: 1), + child: Container( + decoration: BoxDecoration( + color: isSelected + ? Theme.of(context).hoverColor + : Theme.of(context).cardColor, + borderRadius: BorderRadius.all( + Radius.circular(5.0), ), ), - ], - ), + key: ValueKey(entry.name), + height: kDesktopFileTransferRowHeight, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Expanded( + child: InkWell( + child: Row( + children: [ + GestureDetector( + child: Container( + width: kDesktopFileTransferNameColWidth, + child: Tooltip( + waitDuration: + Duration(milliseconds: 500), + message: entry.name, + child: Row(children: [ + entry.isDrive + ? Image( + image: iconHardDrive, + fit: BoxFit.scaleDown, + color: Theme.of(context) + .iconTheme + .color + ?.withOpacity(0.7)) + .paddingAll(4) + : SvgPicture.asset( + entry.isFile + ? "assets/file.svg" + : "assets/folder.svg", + color: Theme.of(context) + .tabBarTheme + .labelColor, + ), + Expanded( + child: Text( + entry.name.nonBreaking, + overflow: + TextOverflow.ellipsis)) + ]), + )), + onTap: () { + final items = getSelectedItems(isLocal); + // handle double click + if (_checkDoubleClick(entry)) { + openDirectory(entry.path, + isLocal: isLocal); + items.clear(); + return; + } + _onSelectedChanged( + items, filteredEntries, entry, isLocal); + }, + ), + GestureDetector( + child: SizedBox( + width: kDesktopFileTransferModifiedColWidth, + child: Tooltip( + waitDuration: + Duration(milliseconds: 500), + message: lastModifiedStr, + child: Text( + lastModifiedStr, + style: TextStyle( + fontSize: 12, + color: MyTheme.darkGray, + ), + )), + ), + ), + SizedBox( + width: 100, + child: GestureDetector( + child: Tooltip( + waitDuration: Duration(milliseconds: 500), + message: sizeStr, + child: Text( + sizeStr, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 10, + color: MyTheme.darkGray), + ), + ), + ), + ), + ], + ), + ), + ), + ], + )), ); }).toList(growable: false); @@ -520,98 +547,156 @@ class _FileManagerPageState extends State Widget statusList() { return PreferredSize( preferredSize: const Size(200, double.infinity), - child: Container( - margin: const EdgeInsets.only(top: 16.0, bottom: 16.0, right: 16.0), - padding: const EdgeInsets.all(8.0), - decoration: BoxDecoration(border: Border.all(color: Colors.grey)), - child: Obx( - () => ListView.builder( - controller: ScrollController(), - itemBuilder: (BuildContext context, int index) { - final item = model.jobTable[index]; - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Transform.rotate( - angle: item.isRemote ? pi : 0, - child: const Icon(Icons.send)), - const SizedBox( - width: 16.0, - ), - Expanded( + child: model.jobTable.isEmpty + ? Center(child: Text(translate("Empty"))) + : Container( + margin: + const EdgeInsets.only(top: 16.0, bottom: 16.0, right: 16.0), + padding: const EdgeInsets.all(8.0), + child: Obx( + () => ListView.builder( + controller: ScrollController(), + itemBuilder: (BuildContext context, int index) { + final item = model.jobTable[index]; + return Padding( + padding: const EdgeInsets.only(bottom: 5), + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + borderRadius: BorderRadius.all( + Radius.circular(15.0), + ), + ), child: Column( mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, children: [ - Tooltip( - waitDuration: Duration(milliseconds: 500), - message: item.jobName, - child: Text( - item.jobName, - maxLines: 1, - overflow: TextOverflow.ellipsis, - )), - Wrap( + Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Text( - '${item.display()} ${max(0, item.fileNum)}/${item.fileCount} '), - Text( - '${translate("files")} ${readableFileSize(item.totalSize.toDouble())} '), - Offstage( - offstage: - item.state != JobState.inProgress, - child: Text( - '${"${readableFileSize(item.speed)}/s"} ')), - Offstage( - offstage: item.totalSize <= 0, - child: Text( - '${(item.finishedSize.toDouble() * 100 / item.totalSize.toDouble()).toStringAsFixed(2)}%'), + Transform.rotate( + angle: item.isRemote ? pi : 0, + child: SvgPicture.asset( + "assets/arrow.svg", + color: Theme.of(context) + .tabBarTheme + .labelColor, + ), + ).paddingOnly(left: 15), + const SizedBox( + width: 16.0, + ), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Tooltip( + waitDuration: + Duration(milliseconds: 500), + message: item.jobName, + child: Text( + item.jobName, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ).paddingSymmetric(vertical: 10), + ), + Text( + '${translate("Total")} ${readableFileSize(item.totalSize.toDouble())}', + style: TextStyle( + fontSize: 12, + color: MyTheme.darkGray, + ), + ), + Offstage( + offstage: + item.state != JobState.inProgress, + child: Text( + '${translate("Speed")} ${readableFileSize(item.speed)}/s', + style: TextStyle( + fontSize: 12, + color: MyTheme.darkGray, + ), + ), + ), + Offstage( + offstage: + item.state == JobState.inProgress, + child: Text( + translate( + item.display(), + ), + style: TextStyle( + fontSize: 12, + color: MyTheme.darkGray, + ), + ), + ), + Offstage( + offstage: + item.state != JobState.inProgress, + child: LinearPercentIndicator( + padding: EdgeInsets.only(right: 15), + animateFromLastPercent: true, + center: Text( + '${(item.finishedSize / item.totalSize * 100).toStringAsFixed(0)}%', + ), + barRadius: Radius.circular(15), + percent: item.finishedSize / + item.totalSize, + progressColor: MyTheme.accent, + backgroundColor: + Theme.of(context).hoverColor, + lineHeight: + kDesktopFileTransferRowHeight, + ).paddingSymmetric(vertical: 15), + ), + ], + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Offstage( + offstage: item.state != JobState.paused, + child: MenuButton( + onPressed: () { + model.resumeJob(item.id); + }, + child: SvgPicture.asset( + "assets/refresh.svg", + color: Colors.white, + ), + color: MyTheme.accent, + hoverColor: MyTheme.accent80, + ), + ), + MenuButton( + padding: EdgeInsets.only(right: 15), + child: SvgPicture.asset( + "assets/close.svg", + color: Colors.white, + ), + onPressed: () { + model.jobTable.removeAt(index); + model.cancelJob(item.id); + }, + color: MyTheme.accent, + hoverColor: MyTheme.accent80, + ), + ], ), ], ), ], - ), + ).paddingSymmetric(vertical: 10), ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Offstage( - offstage: item.state != JobState.paused, - child: IconButton( - onPressed: () { - model.resumeJob(item.id); - }, - splashRadius: kDesktopIconButtonSplashRadius, - icon: const Icon(Icons.restart_alt_rounded)), - ), - IconButton( - icon: const Icon(Icons.close), - splashRadius: 1, - onPressed: () { - model.jobTable.removeAt(index); - model.cancelJob(item.id); - }, - ), - ], - ) - ], - ), - SizedBox( - height: 8.0, - ), - Divider( - height: 2.0, - ) - ], - ); - }, - itemCount: model.jobTable.length, - ), - ), - )); + ); + }, + itemCount: model.jobTable.length, + ), + ), + )); } Widget headTools(bool isLocal) { @@ -620,95 +705,128 @@ class _FileManagerPageState extends State final locationFocus = isLocal ? _locationNodeLocal : _locationNodeRemote; final selectedItems = getSelectedItems(isLocal); return Container( - child: Column( - children: [ - // symbols - PreferredSize( - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - width: 50, - height: 50, - decoration: BoxDecoration(color: Colors.blue), - padding: EdgeInsets.all(8.0), - child: FutureBuilder( - future: bind.sessionGetPlatform( - id: _ffi.id, isRemote: !isLocal), - builder: (context, snapshot) { - if (snapshot.hasData && snapshot.data!.isNotEmpty) { - return getPlatformImage('${snapshot.data}'); - } else { - return CircularProgressIndicator( - color: Colors.white, - ); - } - })), - Text(isLocal - ? translate("Local Computer") - : translate("Remote Computer")) - .marginOnly(left: 8.0) - ], - ), - preferredSize: Size(double.infinity, 70)), - // buttons - Row( - children: [ - Row( - children: [ - IconButton( - icon: const Icon(Icons.arrow_back), - splashRadius: kDesktopIconButtonSplashRadius, - onPressed: () { - selectedItems.clear(); - model.goBack(isLocal: isLocal); - }, - ), - IconButton( - icon: const Icon(Icons.arrow_upward), - splashRadius: kDesktopIconButtonSplashRadius, - onPressed: () { - selectedItems.clear(); - model.goToParentDirectory(isLocal: isLocal); - }, - ), - ], - ), - Expanded( - child: GestureDetector( - onTap: () { - locationStatus.value = - locationStatus.value == LocationStatus.bread - ? LocationStatus.pathLocation - : LocationStatus.bread; - Future.delayed(Duration.zero, () { - if (locationStatus.value == LocationStatus.pathLocation) { - locationFocus.requestFocus(); - } - }); - }, - child: Obx(() => Container( - decoration: BoxDecoration( - border: Border.all( - color: locationStatus.value == LocationStatus.bread - ? Colors.black12 - : Theme.of(context) - .colorScheme - .primary - .withOpacity(0.5))), + child: Column( + children: [ + // symbols + PreferredSize( child: Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Expanded( - child: locationStatus.value == LocationStatus.bread - ? buildBread(isLocal) - : buildPathLocation(isLocal)), + Container( + width: 50, + height: 50, + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(8)), + color: MyTheme.accent, + ), + padding: EdgeInsets.all(8.0), + child: FutureBuilder( + future: bind.sessionGetPlatform( + id: _ffi.id, isRemote: !isLocal), + builder: (context, snapshot) { + if (snapshot.hasData && + snapshot.data!.isNotEmpty) { + return getPlatformImage('${snapshot.data}'); + } else { + return CircularProgressIndicator( + color: Theme.of(context) + .tabBarTheme + .labelColor, + ); + } + })), + Text(isLocal + ? translate("Local Computer") + : translate("Remote Computer")) + .marginOnly(left: 8.0) ], - ))), - )), - Obx(() { - switch (locationStatus.value) { - case LocationStatus.bread: - return IconButton( + ), + preferredSize: Size(double.infinity, 70)) + .paddingOnly(bottom: 15), + // buttons + Row( + children: [ + Row( + children: [ + MenuButton( + padding: EdgeInsets.only( + right: 3, + ), + child: SvgPicture.asset( + "assets/arrow.svg", + color: Theme.of(context).tabBarTheme.labelColor, + ), + color: Theme.of(context).cardColor, + hoverColor: Theme.of(context).hoverColor, + onPressed: () { + selectedItems.clear(); + model.goBack(isLocal: isLocal); + }, + ), + MenuButton( + child: RotatedBox( + quarterTurns: 3, + child: SvgPicture.asset( + "assets/arrow.svg", + color: Theme.of(context).tabBarTheme.labelColor, + ), + ), + color: Theme.of(context).cardColor, + hoverColor: Theme.of(context).hoverColor, + onPressed: () { + selectedItems.clear(); + model.goToParentDirectory(isLocal: isLocal); + }, + ), + ], + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 3.0), + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + borderRadius: BorderRadius.all( + Radius.circular(8.0), + ), + ), + child: Padding( + padding: EdgeInsets.symmetric(vertical: 2.5), + child: GestureDetector( + onTap: () { + locationStatus.value = + locationStatus.value == LocationStatus.bread + ? LocationStatus.pathLocation + : LocationStatus.bread; + Future.delayed(Duration.zero, () { + if (locationStatus.value == + LocationStatus.pathLocation) { + locationFocus.requestFocus(); + } + }); + }, + child: Obx( + () => Container( + child: Row( + children: [ + Expanded( + child: locationStatus.value == + LocationStatus.bread + ? buildBread(isLocal) + : buildPathLocation(isLocal)), + ], + ), + ), + ), + ), + ), + ), + ), + ), + Obx(() { + switch (locationStatus.value) { + case LocationStatus.bread: + return MenuButton( onPressed: () { locationStatus.value = LocationStatus.fileSearchBar; final focusNode = @@ -716,49 +834,77 @@ class _FileManagerPageState extends State Future.delayed( Duration.zero, () => focusNode.requestFocus()); }, - splashRadius: kDesktopIconButtonSplashRadius, - icon: Icon(Icons.search)); - case LocationStatus.pathLocation: - return IconButton( - color: Theme.of(context).disabledColor, + child: SvgPicture.asset( + "assets/search.svg", + color: Theme.of(context).tabBarTheme.labelColor, + ), + color: Theme.of(context).cardColor, + hoverColor: Theme.of(context).hoverColor, + ); + case LocationStatus.pathLocation: + return MenuButton( onPressed: null, - splashRadius: kDesktopIconButtonSplashRadius, - icon: Icon(Icons.close)); - case LocationStatus.fileSearchBar: - return IconButton( + child: SvgPicture.asset( + "assets/close.svg", + color: Theme.of(context).tabBarTheme.labelColor, + ), color: Theme.of(context).disabledColor, + hoverColor: Theme.of(context).hoverColor, + ); + case LocationStatus.fileSearchBar: + return MenuButton( onPressed: () { onSearchText("", isLocal); locationStatus.value = LocationStatus.bread; }, - splashRadius: 1, - icon: Icon(Icons.close)); - } - }), - IconButton( + child: SvgPicture.asset( + "assets/close.svg", + color: Theme.of(context).tabBarTheme.labelColor, + ), + color: Theme.of(context).cardColor, + hoverColor: Theme.of(context).hoverColor, + ); + } + }), + MenuButton( + padding: EdgeInsets.only( + left: 3, + ), onPressed: () { model.refresh(isLocal: isLocal); }, - splashRadius: kDesktopIconButtonSplashRadius, - icon: const Icon(Icons.refresh)), - ], - ), - Row( - textDirection: isLocal ? TextDirection.ltr : TextDirection.rtl, - children: [ - Expanded( - child: Row( - mainAxisAlignment: - isLocal ? MainAxisAlignment.start : MainAxisAlignment.end, - children: [ - IconButton( - onPressed: () { - model.goHome(isLocal: isLocal); - }, - icon: const Icon(Icons.home_outlined), - splashRadius: kDesktopIconButtonSplashRadius, - ), - IconButton( + child: SvgPicture.asset( + "assets/refresh.svg", + color: Theme.of(context).tabBarTheme.labelColor, + ), + color: Theme.of(context).cardColor, + hoverColor: Theme.of(context).hoverColor, + ), + ], + ), + Row( + textDirection: isLocal ? TextDirection.ltr : TextDirection.rtl, + children: [ + Expanded( + child: Row( + mainAxisAlignment: + isLocal ? MainAxisAlignment.start : MainAxisAlignment.end, + children: [ + MenuButton( + padding: EdgeInsets.only( + right: 3, + ), + onPressed: () { + model.goHome(isLocal: isLocal); + }, + child: SvgPicture.asset( + "assets/home.svg", + color: Theme.of(context).tabBarTheme.labelColor, + ), + color: Theme.of(context).cardColor, + hoverColor: Theme.of(context).hoverColor, + ), + MenuButton( onPressed: () { final name = TextEditingController(); _ffi.dialogManager.show((setState, close) { @@ -800,9 +946,14 @@ class _FileManagerPageState extends State ); }); }, - splashRadius: kDesktopIconButtonSplashRadius, - icon: const Icon(Icons.create_new_folder_outlined)), - IconButton( + child: SvgPicture.asset( + "assets/folder_new.svg", + color: Theme.of(context).tabBarTheme.labelColor, + ), + color: Theme.of(context).cardColor, + hoverColor: Theme.of(context).hoverColor, + ), + MenuButton( onPressed: validItems(selectedItems) ? () async { await (model.removeAction(selectedItems, @@ -810,32 +961,80 @@ class _FileManagerPageState extends State selectedItems.clear(); } : null, - splashRadius: kDesktopIconButtonSplashRadius, - icon: const Icon(Icons.delete_forever_outlined)), - menu(isLocal: isLocal), - ], + child: SvgPicture.asset( + "assets/trash.svg", + color: Theme.of(context).tabBarTheme.labelColor, + ), + color: Theme.of(context).cardColor, + hoverColor: Theme.of(context).hoverColor, + ), + menu(isLocal: isLocal), + ], + ), ), - ), - TextButton.icon( + ElevatedButton.icon( + style: ButtonStyle( + padding: MaterialStateProperty.all(isLocal + ? EdgeInsets.only(left: 10) + : EdgeInsets.only(right: 10)), + backgroundColor: MaterialStateProperty.all( + selectedItems.length == 0 + ? MyTheme.accent80 + : MyTheme.accent, + ), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + ), + ), + ), onPressed: validItems(selectedItems) ? () { model.sendFiles(selectedItems, isRemote: !isLocal); selectedItems.clear(); } : null, - icon: Transform.rotate( - angle: isLocal ? 0 : pi, - child: const Icon( - Icons.send, - ), - ), - label: Text( - isLocal ? translate('Send') : translate('Receive'), - )), - ], - ).marginOnly(top: 8.0) - ], - )); + icon: isLocal + ? Text( + translate('Send'), + textAlign: TextAlign.right, + style: TextStyle( + color: selectedItems.length == 0 + ? MyTheme.darkGray + : Colors.white, + ), + ) + : RotatedBox( + quarterTurns: 2, + child: SvgPicture.asset( + "assets/arrow.svg", + color: selectedItems.length == 0 + ? MyTheme.darkGray + : Colors.white, + alignment: Alignment.bottomRight, + ), + ), + label: isLocal + ? SvgPicture.asset( + "assets/arrow.svg", + color: selectedItems.length == 0 + ? MyTheme.darkGray + : Colors.white, + ) + : Text( + translate('Receive'), + style: TextStyle( + color: selectedItems.length == 0 + ? MyTheme.darkGray + : Colors.white, + ), + ), + ), + ], + ).marginOnly(top: 8.0) + ], + ), + ); } bool validItems(SelectedItems items) { @@ -890,25 +1089,27 @@ class _FileManagerPageState extends State mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - child: Listener( - // handle mouse wheel - onPointerSignal: (e) { - if (e is PointerScrollEvent) { - final sc = getBreadCrumbScrollController(isLocal); - final scale = Platform.isWindows ? 2 : 4; - sc.jumpTo(sc.offset + e.scrollDelta.dy / scale); - } - }, - child: BreadCrumb( - items: items, - divider: Icon(Icons.chevron_right), - overflow: ScrollableOverflow( - controller: - getBreadCrumbScrollController(isLocal)), - ))), + child: Listener( + // handle mouse wheel + onPointerSignal: (e) { + if (e is PointerScrollEvent) { + final sc = getBreadCrumbScrollController(isLocal); + final scale = Platform.isWindows ? 2 : 4; + sc.jumpTo(sc.offset + e.scrollDelta.dy / scale); + } + }, + child: BreadCrumb( + items: items, + divider: const Icon(Icons.keyboard_arrow_right_rounded), + overflow: ScrollableOverflow( + controller: getBreadCrumbScrollController(isLocal), + ), + ), + ), + ), ActionIcon( message: "", - icon: Icons.arrow_drop_down, + icon: Icons.keyboard_arrow_down_rounded, onTap: () async { final renderBox = locationBarKey.currentContext ?.findRenderObject() as RenderBox; @@ -1021,13 +1222,23 @@ class _FileManagerPageState extends State .marginSymmetric(horizontal: 4))); } else { final list = PathUtil.split(path, isWindows); - breadCrumbList.addAll(list.asMap().entries.map((e) => BreadCrumbItem( - content: TextButton( + breadCrumbList.addAll( + list.asMap().entries.map( + (e) => BreadCrumbItem( + content: TextButton( child: Text(e.value), style: ButtonStyle( - minimumSize: MaterialStateProperty.all(Size(0, 0))), - onPressed: () => onPressed(list.sublist(0, e.key + 1))) - .marginSymmetric(horizontal: 4)))); + minimumSize: MaterialStateProperty.all( + Size(0, 0), + ), + ), + onPressed: () => onPressed( + list.sublist(0, e.key + 1), + ), + ).marginSymmetric(horizontal: 4), + ), + ), + ); } return breadCrumbList; } @@ -1054,29 +1265,35 @@ class _FileManagerPageState extends State : searchTextObs.value; final textController = TextEditingController(text: text) ..selection = TextSelection.collapsed(offset: text.length); - return Row(children: [ - Icon( - locationStatus.value == LocationStatus.pathLocation - ? Icons.folder - : Icons.search, - color: Theme.of(context).hintColor, - ).paddingSymmetric(horizontal: 2), - Expanded( + return Row( + children: [ + SvgPicture.asset( + locationStatus.value == LocationStatus.pathLocation + ? "assets/folder.svg" + : "assets/search.svg", + color: Theme.of(context).tabBarTheme.labelColor, + ), + Expanded( child: TextField( - focusNode: focusNode, - decoration: InputDecoration( - border: InputBorder.none, - isDense: true, - prefix: Padding(padding: EdgeInsets.only(left: 4.0))), - controller: textController, - onSubmitted: (path) { - openDirectory(path, isLocal: isLocal); - }, - onChanged: locationStatus.value == LocationStatus.fileSearchBar - ? (searchText) => onSearchText(searchText, isLocal) - : null, - )) - ]); + focusNode: focusNode, + decoration: InputDecoration( + border: InputBorder.none, + isDense: true, + prefix: Padding( + padding: EdgeInsets.only(left: 4.0), + ), + ), + controller: textController, + onSubmitted: (path) { + openDirectory(path, isLocal: isLocal); + }, + onChanged: locationStatus.value == LocationStatus.fileSearchBar + ? (searchText) => onSearchText(searchText, isLocal) + : null, + ), + ) + ], + ); } onSearchText(String searchText, bool isLocal) { @@ -1145,12 +1362,13 @@ class _FileManagerPageState extends State Text( name, style: headerTextStyle, - ).marginSymmetric( - horizontal: sortBy == SortBy.name ? 4 : 0.0), + ).marginSymmetric(horizontal: 4), ascending.value != null - ? Icon(ascending.value! - ? Icons.arrow_upward - : Icons.arrow_downward) + ? Icon( + ascending.value! + ? Icons.keyboard_arrow_up_rounded + : Icons.keyboard_arrow_down_rounded, + ) : const Offstage() ], ), diff --git a/flutter/lib/desktop/pages/file_manager_tab_page.dart b/flutter/lib/desktop/pages/file_manager_tab_page.dart index 7540f7662..bbe2b28be 100644 --- a/flutter/lib/desktop/pages/file_manager_tab_page.dart +++ b/flutter/lib/desktop/pages/file_manager_tab_page.dart @@ -86,18 +86,14 @@ class _FileManagerTabPageState extends State { @override Widget build(BuildContext context) { - final tabWidget = Container( - decoration: BoxDecoration( - border: Border.all(color: MyTheme.color(context).border!)), - child: Scaffold( - backgroundColor: Theme.of(context).backgroundColor, - body: DesktopTab( - controller: tabController, - onWindowCloseButton: handleWindowCloseButton, - tail: const AddButton().paddingOnly(left: 10), - labelGetter: DesktopTab.labelGetterAlias, - )), - ); + final tabWidget = Scaffold( + backgroundColor: Theme.of(context).cardColor, + body: DesktopTab( + controller: tabController, + onWindowCloseButton: handleWindowCloseButton, + tail: const AddButton().paddingOnly(left: 10), + labelGetter: DesktopTab.labelGetterAlias, + )); return Platform.isMacOS ? tabWidget : SubWindowDragToResizeArea( diff --git a/flutter/lib/desktop/widgets/menu_button.dart b/flutter/lib/desktop/widgets/menu_button.dart index 96cc9fa9b..df2c48ab4 100644 --- a/flutter/lib/desktop/widgets/menu_button.dart +++ b/flutter/lib/desktop/widgets/menu_button.dart @@ -27,6 +27,7 @@ class MenuButton extends StatefulWidget { class _MenuButtonState extends State { bool _isHover = false; + final double _borderRadius = 8.0; @override Widget build(BuildContext context) { @@ -38,16 +39,17 @@ class _MenuButtonState extends State { type: MaterialType.transparency, child: Ink( decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), + borderRadius: BorderRadius.circular(_borderRadius), color: _isHover ? widget.hoverColor : widget.color, ), child: InkWell( + hoverColor: widget.hoverColor, onHover: (val) { setState(() { _isHover = val; }); }, - borderRadius: BorderRadius.circular(5), + borderRadius: BorderRadius.circular(_borderRadius), splashColor: widget.splashColor, enableFeedback: widget.enableFeedback, onTap: widget.onPressed, diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index e6ec0454d..a07df9c2e 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -325,8 +325,8 @@ packages: dependency: "direct main" description: path: "." - ref: bc8604a88e52b2b6e64d2661ae49a71450a47af8 - resolved-ref: bc8604a88e52b2b6e64d2661ae49a71450a47af8 + ref: f37357ed98a10717576eb9ed8413e92b2ec5d13a + resolved-ref: f37357ed98a10717576eb9ed8413e92b2ec5d13a url: "https://github.com/Kingtous/rustdesk_desktop_multi_window" source: git version: "0.1.0" @@ -970,6 +970,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.1" + percent_indicator: + dependency: "direct main" + description: + name: percent_indicator + sha256: cec41f67181fbd5322aa68b355621d1a4eea827426b8eeb613f6cbe195ff7b4a + url: "https://pub.dev" + source: hosted + version: "4.2.2" petitparser: dependency: transitive description: @@ -1216,6 +1224,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + texture_rgba_renderer: + dependency: "direct main" + description: + name: texture_rgba_renderer + sha256: fbb09b2c6b4ce71261927f9e7e4ea339af3e2f3f2b175f6fb921de1c66ec848d + url: "https://pub.dev" + source: hosted + version: "0.0.8" timing: dependency: transitive description: diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index e009ea890..572b3e20a 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -19,156 +19,153 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev version: 1.2.0 environment: - sdk: ">=2.17.0" + sdk: ">=2.17.0" dependencies: - flutter: - sdk: flutter - flutter_localizations: - sdk: flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.3 - ffi: ^2.0.1 - path_provider: ^2.0.12 - external_path: ^1.0.1 - provider: ^6.0.3 - tuple: ^2.0.0 - wakelock: ^0.6.2 - device_info_plus: ^4.1.2 - #firebase_analytics: ^9.1.5 - package_info_plus: ^1.4.2 - url_launcher: ^6.0.9 - toggle_switch: ^1.4.0 - dash_chat_2: ^0.0.14 - draggable_float_widget: ^0.0.2 - settings_ui: ^2.0.2 - flutter_breadcrumb: ^1.0.1 - http: ^0.13.4 - qr_code_scanner: ^1.0.0 - zxing2: ^0.1.0 - image_picker: ^0.8.5 - image: ^3.1.3 - back_button_interceptor: ^6.0.1 - flutter_rust_bridge: ^1.61.1 - window_manager: - git: - url: https://github.com/Kingtous/rustdesk_window_manager - ref: 32b24c66151b72bba033ef8b954486aa9351d97b - desktop_multi_window: - git: - url: https://github.com/Kingtous/rustdesk_desktop_multi_window - ref: f37357ed98a10717576eb9ed8413e92b2ec5d13a - freezed_annotation: ^2.0.3 - flutter_custom_cursor: ^0.0.4 - window_size: - git: - url: https://github.com/google/flutter-desktop-embedding.git - path: plugins/window_size - ref: a738913c8ce2c9f47515382d40827e794a334274 - get: ^4.6.5 - visibility_detector: ^0.3.3 - contextmenu: ^3.0.0 - desktop_drop: ^0.3.3 - scroll_pos: ^0.3.0 - debounce_throttle: ^2.0.0 - file_picker: ^5.1.0 - flutter_svg: ^1.1.5 - flutter_improved_scrolling: - # currently, we use flutter 3.0.5 for windows build, latest for other builds. - # - # for flutter 3.0.5, please use official version(just comment code below). - # if build rustdesk by flutter >=3.3, please use our custom pub below (uncomment code below). - git: - url: https://github.com/Kingtous/flutter_improved_scrolling - ref: 62f09545149f320616467c306c8c5f71714a18e6 - uni_links: ^0.5.1 - uni_links_desktop: ^0.1.4 - path: ^1.8.1 - auto_size_text: ^3.0.0 - bot_toast: ^4.0.3 - win32: any - password_strength: ^0.2.0 - flutter_launcher_icons: ^0.11.0 - flutter_keyboard_visibility: ^5.4.0 - texture_rgba_renderer: ^0.0.8 + flutter: + sdk: flutter + flutter_localizations: + sdk: flutter + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.3 + ffi: ^2.0.1 + path_provider: ^2.0.12 + external_path: ^1.0.1 + provider: ^6.0.3 + tuple: ^2.0.0 + wakelock: ^0.6.2 + device_info_plus: ^4.1.2 + #firebase_analytics: ^9.1.5 + package_info_plus: ^1.4.2 + url_launcher: ^6.0.9 + toggle_switch: ^1.4.0 + dash_chat_2: ^0.0.14 + draggable_float_widget: ^0.0.2 + settings_ui: ^2.0.2 + flutter_breadcrumb: ^1.0.1 + http: ^0.13.4 + qr_code_scanner: ^1.0.0 + zxing2: ^0.1.0 + image_picker: ^0.8.5 + image: ^3.1.3 + back_button_interceptor: ^6.0.1 + flutter_rust_bridge: ^1.61.1 + window_manager: + git: + url: https://github.com/Kingtous/rustdesk_window_manager + ref: 32b24c66151b72bba033ef8b954486aa9351d97b + desktop_multi_window: + git: + url: https://github.com/Kingtous/rustdesk_desktop_multi_window + ref: f37357ed98a10717576eb9ed8413e92b2ec5d13a + freezed_annotation: ^2.0.3 + flutter_custom_cursor: ^0.0.4 + window_size: + git: + url: https://github.com/google/flutter-desktop-embedding.git + path: plugins/window_size + ref: a738913c8ce2c9f47515382d40827e794a334274 + get: ^4.6.5 + visibility_detector: ^0.3.3 + contextmenu: ^3.0.0 + desktop_drop: ^0.3.3 + scroll_pos: ^0.3.0 + debounce_throttle: ^2.0.0 + file_picker: ^5.1.0 + flutter_svg: ^1.1.5 + flutter_improved_scrolling: + # currently, we use flutter 3.0.5 for windows build, latest for other builds. + # + # for flutter 3.0.5, please use official version(just comment code below). + # if build rustdesk by flutter >=3.3, please use our custom pub below (uncomment code below). + git: + url: https://github.com/Kingtous/flutter_improved_scrolling + ref: 62f09545149f320616467c306c8c5f71714a18e6 + uni_links: ^0.5.1 + uni_links_desktop: ^0.1.4 + path: ^1.8.1 + auto_size_text: ^3.0.0 + bot_toast: ^4.0.3 + win32: any + password_strength: ^0.2.0 + flutter_launcher_icons: ^0.11.0 + flutter_keyboard_visibility: ^5.4.0 + texture_rgba_renderer: ^0.0.8 + percent_indicator: ^4.2.2 dev_dependencies: - icons_launcher: ^2.0.4 - #flutter_test: - #sdk: flutter - build_runner: ^2.1.11 - freezed: ^2.0.3 - flutter_lints: ^2.0.0 - ffigen: ^7.2.4 + icons_launcher: ^2.0.4 + #flutter_test: + #sdk: flutter + build_runner: ^2.1.11 + freezed: ^2.0.3 + flutter_lints: ^2.0.0 + ffigen: ^7.2.4 # rerun: flutter pub run flutter_launcher_icons flutter_icons: - image_path: "../res/icon.png" - remove_alpha_ios: true - android: true - ios: true - windows: - generate: true - macos: - image_path: "../res/mac-icon.png" - generate: true - linux: true - web: - generate: true - + image_path: "../res/icon.png" + remove_alpha_ios: true + android: true + ios: true + windows: + generate: true + macos: + image_path: "../res/mac-icon.png" + generate: true + linux: true + web: + generate: true # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter. flutter: - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true - # To add assets to your application, add an assets section, like this: - assets: - - assets/ + # To add assets to your application, add an assets section, like this: + assets: + - assets/ - fonts: - - family: GestureIcons - fonts: - - asset: assets/gestures.ttf - - family: Tabbar - fonts: - - asset: assets/tabbar.ttf - - family: PeerSearchbar - fonts: - - asset: assets/peer_searchbar.ttf + fonts: + - family: GestureIcons + fonts: + - asset: assets/gestures.ttf + - family: Tabbar + fonts: + - asset: assets/tabbar.ttf + - family: PeerSearchbar + fonts: + - asset: assets/peer_searchbar.ttf - + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages