import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:gitjournal/folder_views/common.dart'; import 'package:gitjournal/utils.dart'; import 'package:gitjournal/widgets/editor_scroll_view.dart'; import 'package:gitjournal/widgets/notes_backlinks.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:path/path.dart' as p; import 'package:gitjournal/core/note.dart'; import 'package:gitjournal/core/notes_folder_fs.dart'; class NoteViewer extends StatelessWidget { final Note note; const NoteViewer({Key key, @required this.note}) : super(key: key); @override Widget build(BuildContext context) { ThemeData theme = Theme.of(context); theme = theme.copyWith( textTheme: theme.textTheme.copyWith( subtitle1: theme.textTheme.subtitle1, ), ); // Copied from MarkdownStyleSheet except Grey is replaced with Highlight color var markdownStyleSheet = MarkdownStyleSheet.fromTheme(theme).copyWith( code: theme.textTheme.bodyText2.copyWith( backgroundColor: theme.dialogBackgroundColor, fontFamily: "monospace", fontSize: theme.textTheme.bodyText2.fontSize * 0.85, ), tableBorder: TableBorder.all(color: theme.highlightColor, width: 0), tableCellsDecoration: BoxDecoration(color: theme.dialogBackgroundColor), codeblockDecoration: BoxDecoration( color: theme.dialogBackgroundColor, borderRadius: BorderRadius.circular(2.0), ), horizontalRuleDecoration: BoxDecoration( border: Border( top: BorderSide(width: 5.0, color: theme.highlightColor), ), ), ); final rootFolder = Provider.of(context); var view = EditorScrollView( child: Column( children: [ if (note.canHaveMetadata) NoteTitleHeader(note.title), Padding( padding: const EdgeInsets.only(top: 8.0, bottom: 8.0), child: MarkdownBody( data: note.body, styleSheet: markdownStyleSheet, onTapLink: (String link) { if (link.startsWith('./')) { var spec = link.substring(2); var note = rootFolder.getNoteWithSpec(spec); if (note != null) { openNoteEditor(context, note); } else { showSnackbar(context, "Link '$link' not found"); } } else { print("Launching " + link); launch(link); } }, imageBuilder: (url) => kDefaultImageBuilder( url, note.parent.folderPath + p.separator, null, null), ), ), NoteBacklinkRenderer(note: note, rootFolder: rootFolder), // _buildFooter(context), ], crossAxisAlignment: CrossAxisAlignment.start, ), ); return view; } /* Widget _buildFooter(BuildContext context) { return Padding( padding: const EdgeInsets.only(top: 8.0, bottom: 8.0), child: Row( children: [ IconButton( icon: Icon(Icons.arrow_left), tooltip: 'Previous Entry', onPressed: showPrevNoteFunc, ), Expanded( flex: 10, child: Text(''), ), IconButton( icon: Icon(Icons.arrow_right), tooltip: 'Next Entry', onPressed: showNextNoteFunc, ), ], ), ); } */ } class NoteTitleHeader extends StatelessWidget { final String header; NoteTitleHeader(this.header); @override Widget build(BuildContext context) { var textTheme = Theme.of(context).textTheme; return Padding( padding: const EdgeInsets.only(top: 8.0, bottom: 8.0), child: Text(header, style: textTheme.headline6), ); } } // // Copied from flutter_markdown // But it uses CachedNetworkImage // typedef Widget ImageBuilder( Uri uri, String imageDirectory, double width, double height); final ImageBuilder kDefaultImageBuilder = ( Uri uri, String imageDirectory, double width, double height, ) { if (uri.scheme == 'http' || uri.scheme == 'https') { return CachedNetworkImage( imageUrl: uri.toString(), width: width, height: height, placeholder: (context, url) => const CircularProgressIndicator(), errorWidget: (context, url, error) => const Icon(Icons.error), ); } else if (uri.scheme == 'data') { return _handleDataSchemeUri(uri, width, height); } else if (uri.scheme == "resource") { return Image.asset(uri.path, width: width, height: height); } else { Uri fileUri = imageDirectory != null ? Uri.parse(imageDirectory + uri.toString()) : uri; if (fileUri.scheme == 'http' || fileUri.scheme == 'https') { return CachedNetworkImage( imageUrl: fileUri.toString(), width: width, height: height, placeholder: (context, url) => const CircularProgressIndicator(), errorWidget: (context, url, error) => const Icon(Icons.error), ); } else { return Image.file(File.fromUri(fileUri), width: width, height: height); } } }; Widget _handleDataSchemeUri(Uri uri, final double width, final double height) { final String mimeType = uri.data.mimeType; if (mimeType.startsWith('image/')) { return Image.memory( uri.data.contentAsBytes(), width: width, height: height, ); } else if (mimeType.startsWith('text/')) { return Text(uri.data.contentAsString()); } return const SizedBox(); }