1+ import 'dart:async' ;
12import 'dart:convert' ;
23import 'dart:io' ;
34
45import 'package:file_picker/file_picker.dart' ;
56import 'package:flutter/foundation.dart' ;
67import 'package:flutter/material.dart' ;
78import 'package:flutter/services.dart' ;
9+ import 'package:htmltopdfwidgets/htmltopdfwidgets.dart' as html2pdf;
10+ import 'package:markdown/markdown.dart' as md;
811import 'package:markdown_editor/device_preference_notifier.dart' ;
912import 'package:markdown_editor/l10n/generated/app_localizations.dart' ;
1013import 'package:markdown_editor/widgets/MarkdownBody/custom_image_config.dart' ;
1114import 'package:markdown_editor/widgets/MarkdownBody/custom_text_node.dart' ;
1215import 'package:markdown_editor/widgets/MarkdownBody/latex_node.dart' ;
1316import 'package:markdown_editor/widgets/MarkdownTextInput/markdown_text_input.dart' ;
1417import 'package:markdown_widget/markdown_widget.dart' ;
18+ import 'package:pdf/widgets.dart' as pw;
19+ import 'package:printing/printing.dart' ;
20+ import 'package:url_launcher/url_launcher.dart' ;
1521
16- enum MenuItem { switchTheme, switchView, open, clear, save }
22+ enum MenuItem { switchTheme, switchView, open, clear, save, print, donate }
1723
1824class Home extends StatefulWidget {
1925 final DevicePreferenceNotifier devicePreferenceNotifier;
@@ -95,6 +101,8 @@ class _HomeState extends State<Home> {
95101 try {
96102 final FilePickerResult ? result = await FilePicker .platform.pickFiles (
97103 type: FileType .custom,
104+ initialDirectory:
105+ widget.devicePreferenceNotifier.value.defaultFolderPath,
98106 allowedExtensions: ['md' ],
99107 );
100108 if (result != null ) {
@@ -107,6 +115,13 @@ class _HomeState extends State<Home> {
107115 _inputText = await file.readAsString ();
108116 _textEditingController.text = _inputText;
109117 setState (() {});
118+ final folderPath = _filePath.substring (
119+ 0 ,
120+ _filePath.lastIndexOf (Platform .pathSeparator),
121+ );
122+ unawaited (
123+ widget.devicePreferenceNotifier.setDefaultFolderPath (folderPath),
124+ );
110125 }
111126 } catch (e) {
112127 if (mounted) {
@@ -174,13 +189,46 @@ class _HomeState extends State<Home> {
174189 );
175190 return ;
176191 } else {
177- await FilePicker .platform.saveFile (
192+ final filePath = await FilePicker .platform.saveFile (
178193 dialogTitle: AppLocalizations .of (context)! .saveFileDialogTitle,
179194 fileName: (! kIsWeb && Platform .isWindows) ? null : "$_fileName .md" ,
195+ initialDirectory:
196+ widget.devicePreferenceNotifier.value.defaultFolderPath,
180197 type: FileType .custom,
181198 allowedExtensions: ['md' ],
182199 bytes: utf8.encode (_inputText),
183200 );
201+ if (filePath != null ) {
202+ final folderPath = filePath.substring (
203+ 0 ,
204+ filePath.lastIndexOf (Platform .pathSeparator),
205+ );
206+ unawaited (
207+ widget.devicePreferenceNotifier.setDefaultFolderPath (folderPath),
208+ );
209+ }
210+ }
211+ }
212+
213+ Future <void > _printFile () async {
214+ if (_inputText.isEmpty) {
215+ ScaffoldMessenger .of (context).showSnackBar (
216+ SnackBar (
217+ content: Text (AppLocalizations .of (context)! .emptyInputTextContent),
218+ ),
219+ );
220+ return ;
221+ } else {
222+ final htmlFromMarkdown = md.markdownToHtml (_inputText);
223+ await Printing .layoutPdf (
224+ usePrinterSettings: true ,
225+ onLayout: (format) async {
226+ final pdf = pw.Document ();
227+ final widgets = await html2pdf.HTMLToPdf ().convert (htmlFromMarkdown);
228+ pdf.addPage (pw.MultiPage (build: (context) => widgets));
229+ return pdf.save ();
230+ },
231+ );
184232 }
185233 }
186234
@@ -272,18 +320,28 @@ class _HomeState extends State<Home> {
272320 child: AnimatedSwitcher (
273321 duration: const Duration (milliseconds: 300 ),
274322 reverseDuration: const Duration (milliseconds: 300 ),
275- child: _isPreview
276- ? _markdownPreviewWidget ()
277- : MarkdownTextInput (
278- (String value) {
279- setState (() {
280- _inputText = value;
281- });
282- },
283- _inputText,
284- controller: _textEditingController,
285- label: AppLocalizations .of (context)! .markdownTextInputLabel,
286- ),
323+ child: GestureDetector (
324+ onHorizontalDragEnd: (drag) {
325+ if (drag.primaryVelocity == null ) {
326+ return ;
327+ }
328+ setState (() {
329+ _isPreview = ! _isPreview;
330+ });
331+ },
332+ child: _isPreview
333+ ? _markdownPreviewWidget ()
334+ : MarkdownTextInput (
335+ (String value) {
336+ setState (() {
337+ _inputText = value;
338+ });
339+ },
340+ _inputText,
341+ controller: _textEditingController,
342+ label: AppLocalizations .of (context)! .markdownTextInputLabel,
343+ ),
344+ ),
287345 ),
288346 );
289347 }
@@ -334,6 +392,15 @@ class _HomeState extends State<Home> {
334392 case MenuItem .save:
335393 await _saveFile ();
336394 break ;
395+ case MenuItem .print:
396+ await _printFile ();
397+ break ;
398+ case MenuItem .donate:
399+ await launchUrl (
400+ Uri .parse ("https://buymeacoffee.com/adeeteya" ),
401+ mode: LaunchMode .externalApplication,
402+ );
403+ break ;
337404 }
338405 },
339406 itemBuilder: (context) => [
@@ -391,6 +458,26 @@ class _HomeState extends State<Home> {
391458 ],
392459 ),
393460 ),
461+ PopupMenuItem (
462+ value: MenuItem .print,
463+ child: Row (
464+ children: [
465+ const Icon (Icons .print),
466+ const SizedBox (width: 8 ),
467+ Text (AppLocalizations .of (context)! .print),
468+ ],
469+ ),
470+ ),
471+ PopupMenuItem (
472+ value: MenuItem .donate,
473+ child: Row (
474+ children: [
475+ const Icon (Icons .volunteer_activism),
476+ const SizedBox (width: 8 ),
477+ Text (AppLocalizations .of (context)! .donate),
478+ ],
479+ ),
480+ ),
394481 ],
395482 ),
396483 ],
0 commit comments