folder_widget.dart 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. import 'dart:io';
  2. import 'package:app_flowy/util/file_picker/file_picker_service.dart';
  3. import 'package:easy_localization/easy_localization.dart';
  4. import 'package:flowy_infra_ui/style_widget/text.dart';
  5. import 'package:flowy_infra_ui/style_widget/text_field.dart';
  6. import 'package:flowy_infra_ui/widget/rounded_button.dart';
  7. import 'package:flutter/material.dart';
  8. import 'package:fluttertoast/fluttertoast.dart';
  9. import '../../../generated/locale_keys.g.dart';
  10. import '../../../startup/startup.dart';
  11. import '../../../workspace/application/settings/settings_location_cubit.dart';
  12. import '../../../workspace/presentation/home/toast.dart';
  13. enum _FolderPage {
  14. options,
  15. create,
  16. open,
  17. }
  18. class FolderWidget extends StatefulWidget {
  19. const FolderWidget({
  20. Key? key,
  21. required this.createFolderCallback,
  22. }) : super(key: key);
  23. final Future<void> Function() createFolderCallback;
  24. @override
  25. State<FolderWidget> createState() => _FolderWidgetState();
  26. }
  27. class _FolderWidgetState extends State<FolderWidget> {
  28. var page = _FolderPage.options;
  29. @override
  30. Widget build(BuildContext context) {
  31. return SizedBox(
  32. height: 250,
  33. child: _mapIndexToWidget(context),
  34. );
  35. }
  36. Widget _mapIndexToWidget(BuildContext context) {
  37. switch (page) {
  38. case _FolderPage.options:
  39. return FolderOptionsWidget(
  40. onPressedCreate: () {
  41. setState(() => page = _FolderPage.create);
  42. },
  43. onPressedOpen: () {
  44. _openFolder();
  45. },
  46. );
  47. case _FolderPage.create:
  48. return CreateFolderWidget(
  49. onPressedBack: () {
  50. setState(() => page = _FolderPage.options);
  51. },
  52. onPressedCreate: widget.createFolderCallback,
  53. );
  54. case _FolderPage.open:
  55. return Container();
  56. }
  57. }
  58. Future<void> _openFolder() async {
  59. final directory = await getIt<FilePickerService>().getDirectoryPath();
  60. if (directory != null) {
  61. await getIt<SettingsLocationCubit>().setLocation(directory);
  62. await widget.createFolderCallback();
  63. }
  64. }
  65. }
  66. class FolderOptionsWidget extends StatelessWidget {
  67. const FolderOptionsWidget({
  68. Key? key,
  69. required this.onPressedCreate,
  70. required this.onPressedOpen,
  71. }) : super(key: key);
  72. final VoidCallback onPressedCreate;
  73. final VoidCallback onPressedOpen;
  74. @override
  75. Widget build(BuildContext context) {
  76. return ListView(
  77. shrinkWrap: true,
  78. children: <Widget>[
  79. Card(
  80. child: ListTile(
  81. title: FlowyText.medium(
  82. LocaleKeys.settings_files_createNewFolder.tr(),
  83. ),
  84. subtitle: FlowyText.regular(
  85. LocaleKeys.settings_files_createNewFolderDesc.tr(),
  86. ),
  87. trailing: _buildTextButton(
  88. context,
  89. LocaleKeys.settings_files_create.tr(),
  90. onPressedCreate,
  91. ),
  92. ),
  93. ),
  94. Card(
  95. child: ListTile(
  96. title: FlowyText.medium(
  97. LocaleKeys.settings_files_openFolder.tr(),
  98. ),
  99. subtitle: FlowyText.regular(
  100. LocaleKeys.settings_files_openFolderDesc.tr(),
  101. ),
  102. trailing: _buildTextButton(
  103. context,
  104. LocaleKeys.settings_files_open.tr(),
  105. onPressedOpen,
  106. ),
  107. ),
  108. ),
  109. ],
  110. );
  111. }
  112. }
  113. class CreateFolderWidget extends StatefulWidget {
  114. const CreateFolderWidget({
  115. Key? key,
  116. required this.onPressedBack,
  117. required this.onPressedCreate,
  118. }) : super(key: key);
  119. final VoidCallback onPressedBack;
  120. final Future<void> Function() onPressedCreate;
  121. @override
  122. State<CreateFolderWidget> createState() => CreateFolderWidgetState();
  123. }
  124. @visibleForTesting
  125. class CreateFolderWidgetState extends State<CreateFolderWidget> {
  126. var _folderName = 'appflowy';
  127. @visibleForTesting
  128. var directory = '';
  129. final _fToast = FToast();
  130. @override
  131. void initState() {
  132. super.initState();
  133. _fToast.init(context);
  134. }
  135. @override
  136. Widget build(BuildContext context) {
  137. return Column(
  138. children: [
  139. Align(
  140. alignment: Alignment.centerLeft,
  141. child: TextButton.icon(
  142. onPressed: widget.onPressedBack,
  143. icon: const Icon(Icons.arrow_back_rounded),
  144. label: const Text('Back'),
  145. ),
  146. ),
  147. Card(
  148. child: ListTile(
  149. title: FlowyText.medium(
  150. LocaleKeys.settings_files_location.tr(),
  151. ),
  152. subtitle: FlowyText.regular(
  153. LocaleKeys.settings_files_locationDesc.tr(),
  154. ),
  155. trailing: SizedBox(
  156. width: 100,
  157. height: 36,
  158. child: FlowyTextField(
  159. hintText: LocaleKeys.settings_files_folderHintText.tr(),
  160. onChanged: (name) {
  161. _folderName = name;
  162. },
  163. onSubmitted: (name) {
  164. setState(() {
  165. _folderName = name;
  166. });
  167. },
  168. ),
  169. ),
  170. ),
  171. ),
  172. Card(
  173. child: ListTile(
  174. title: FlowyText.medium(LocaleKeys.settings_files_folderPath.tr()),
  175. subtitle: FlowyText.regular(_path),
  176. trailing: _buildTextButton(
  177. context, LocaleKeys.settings_files_browser.tr(), () async {
  178. final dir = await getIt<FilePickerService>().getDirectoryPath();
  179. if (dir != null) {
  180. setState(() {
  181. directory = dir;
  182. });
  183. }
  184. }),
  185. ),
  186. ),
  187. Card(
  188. child: _buildTextButton(
  189. context, LocaleKeys.settings_files_create.tr(), () async {
  190. if (_path.isEmpty) {
  191. _showToast(LocaleKeys.settings_files_locationCannotBeEmpty.tr());
  192. } else {
  193. await getIt<SettingsLocationCubit>().setLocation(_path);
  194. await widget.onPressedCreate();
  195. }
  196. }),
  197. )
  198. ],
  199. );
  200. }
  201. String get _path {
  202. if (directory.isEmpty) return '';
  203. final String path;
  204. if (Platform.isMacOS) {
  205. path = directory.replaceAll('/Volumes/Macintosh HD', '');
  206. } else {
  207. path = directory;
  208. }
  209. return '$path/$_folderName';
  210. }
  211. void _showToast(String message) {
  212. _fToast.showToast(
  213. child: FlowyMessageToast(message: message),
  214. gravity: ToastGravity.CENTER,
  215. );
  216. }
  217. }
  218. Widget _buildTextButton(
  219. BuildContext context, String title, VoidCallback onPressed) {
  220. return SizedBox(
  221. width: 70,
  222. height: 36,
  223. child: RoundedTextButton(
  224. title: title,
  225. onPressed: onPressed,
  226. ),
  227. );
  228. }