diff --git a/app/lib/main.dart b/app/lib/main.dart index 77622559..f3ec0666 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'pages/home_page.dart'; void main() { + WidgetsFlutterBinding.ensureInitialized(); runApp(MyApp()); } diff --git a/app/lib/pages/home_page.dart b/app/lib/pages/home_page.dart index 3663543f..422d0954 100644 --- a/app/lib/pages/home_page.dart +++ b/app/lib/pages/home_page.dart @@ -89,11 +89,11 @@ class _HomePageState extends State { } Widget _buildWelcomeEditor(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Expanded( - child: Container( + return SafeArea( + child: Stack( + children: [ + Container( + height: MediaQuery.of(context).size.height * 0.88, color: Colors.white, padding: const EdgeInsets.only(left: 16.0, right: 16.0), child: QuillEditor( @@ -120,13 +120,15 @@ class _HomePageState extends State { sizeSmall: TextStyle(fontSize: 9.0)), ), ), - ), - Container( - child: QuillToolbar.basic( - controller: _controller, - uploadFileCallback: _fakeUploadImageCallBack), - ) - ], + Container( + padding: + EdgeInsets.only(top: MediaQuery.of(context).size.height * 0.9), + child: QuillToolbar.basic( + controller: _controller, + uploadFileCallback: _fakeUploadImageCallBack), + ) + ], + ), ); } diff --git a/app/pubspec.lock b/app/pubspec.lock index 25012d30..f4e16a57 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -43,6 +43,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.15.0" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.16.2" cupertino_icons: dependency: "direct main" description: @@ -93,6 +114,13 @@ packages: description: flutter source: sdk version: "0.0.0" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.14.0+4" http: dependency: transitive description: @@ -252,6 +280,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0" + universal_html: + dependency: transitive + description: + name: universal_html + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.4" + universal_io: + dependency: transitive + description: + name: universal_io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" url_launcher: dependency: transitive description: @@ -301,6 +343,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + zone_local: + dependency: transitive + description: + name: zone_local + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" sdks: dart: ">=2.12.0-0.0 <3.0.0" flutter: ">=1.22.0" diff --git a/lib/widgets/FakeUi.dart b/lib/widgets/FakeUi.dart new file mode 100644 index 00000000..bc9799af --- /dev/null +++ b/lib/widgets/FakeUi.dart @@ -0,0 +1,4 @@ +// ignore: camel_case_types +class platformViewRegistry { + static registerViewFactory(String viewId, dynamic cb) {} +} diff --git a/lib/widgets/RealUi.dart b/lib/widgets/RealUi.dart new file mode 100644 index 00000000..c2b8ea23 --- /dev/null +++ b/lib/widgets/RealUi.dart @@ -0,0 +1,9 @@ +import 'dart:ui' as ui; + +// ignore: camel_case_types +class platformViewRegistry { + static registerViewFactory(String viewId, dynamic cb) { + // ignore:undefined_prefixed_name + ui.platformViewRegistry.registerViewFactory(viewId, cb); + } +} diff --git a/lib/widgets/editor.dart b/lib/widgets/editor.dart index 6a388ed8..018ef0b2 100644 --- a/lib/widgets/editor.dart +++ b/lib/widgets/editor.dart @@ -1,7 +1,5 @@ -import 'dart:html' as html; -import 'dart:io'; +import 'dart:io' as io; import 'dart:math' as math; -import 'dart:ui' as ui; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; @@ -19,9 +17,12 @@ import 'package:flutter_quill/models/documents/nodes/line.dart'; import 'package:flutter_quill/models/documents/nodes/node.dart'; import 'package:flutter_quill/widgets/image.dart'; import 'package:flutter_quill/widgets/raw_editor.dart'; +import 'package:flutter_quill/widgets/responsive_widget.dart'; import 'package:flutter_quill/widgets/text_selection.dart'; +import 'package:universal_html/prefer_universal/html.dart' as html; import 'package:url_launcher/url_launcher.dart'; +import 'FakeUi.dart' if (dart.library.html) 'RealUi.dart' as ui; import 'box.dart'; import 'controller.dart'; import 'cursor.dart'; @@ -79,25 +80,43 @@ abstract class RenderAbstractEditor { Widget _defaultEmbedBuilder(BuildContext context, leaf.Embed node) { switch (node.value.type) { case 'image': - if (kIsWeb) { - String imageUrl = node.value.data; + String imageUrl = node.value.data; + return imageUrl.startsWith('http') + ? Image.network(imageUrl) + : Image.file(io.File(imageUrl)); + default: + throw UnimplementedError( + 'Embeddable type "${node.value.type}" is not supported by default embed ' + 'builder of QuillEditor. You must pass your own builder function to ' + 'embedBuilder property of QuillEditor or QuillField widgets.'); + } +} - ui.platformViewRegistry.registerViewFactory( - imageUrl, - (int viewId) => html.ImageElement()..src = imageUrl, - ); - return Container( - constraints: BoxConstraints(maxWidth: 300), - height: MediaQuery.of(context).size.height, +Widget _defaultEmbedBuilderWeb(BuildContext context, leaf.Embed node) { + switch (node.value.type) { + case 'image': + String imageUrl = node.value.data; + Size size = MediaQuery.of(context).size; + ui.platformViewRegistry.registerViewFactory( + imageUrl, + (int viewId) => html.ImageElement()..src = imageUrl, + ); + return Padding( + padding: EdgeInsets.only( + right: ResponsiveWidget.isMediumScreen(context) + ? size.width * 0.5 + : (ResponsiveWidget.isLargeScreen(context)) + ? size.width * 0.75 + : size.width * 0.2, + ), + child: SizedBox( + height: MediaQuery.of(context).size.height * 0.45, child: HtmlElementView( viewType: imageUrl, ), - ); - } - String imageUrl = node.value.data; - return imageUrl.startsWith('http') - ? Image.network(imageUrl) - : Image.file(File(imageUrl)); + ), + ); + default: throw UnimplementedError( 'Embeddable type "${node.value.type}" is not supported by default embed ' @@ -144,7 +163,8 @@ class QuillEditor extends StatefulWidget { this.keyboardAppearance = Brightness.light, this.scrollPhysics, this.onLaunchUrl, - this.embedBuilder = _defaultEmbedBuilder}) + this.embedBuilder = + kIsWeb ? _defaultEmbedBuilderWeb : _defaultEmbedBuilder}) : assert(controller != null), assert(scrollController != null), assert(scrollable != null), @@ -383,7 +403,7 @@ class _QuillEditorSelectionGestureDetectorBuilder builder: (context) => ImageTapWrapper( imageProvider: imageUrl.startsWith('http') ? NetworkImage(imageUrl) - : FileImage(File(blockEmbed.data))), + : FileImage(io.File(blockEmbed.data))), ), ); } diff --git a/lib/widgets/responsive_widget.dart b/lib/widgets/responsive_widget.dart new file mode 100644 index 00000000..a047deab --- /dev/null +++ b/lib/widgets/responsive_widget.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +class ResponsiveWidget extends StatelessWidget { + final Widget largeScreen; + final Widget mediumScreen; + final Widget smallScreen; + + const ResponsiveWidget( + {Key key, + @required this.largeScreen, + this.mediumScreen, + this.smallScreen}) + : super(key: key); + + static bool isSmallScreen(BuildContext context) { + return MediaQuery.of(context).size.width < 800; + } + + static bool isLargeScreen(BuildContext context) { + return MediaQuery.of(context).size.width > 1200; + } + + static bool isMediumScreen(BuildContext context) { + return MediaQuery.of(context).size.width >= 800 && + MediaQuery.of(context).size.width <= 1200; + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxWidth > 1200) { + return largeScreen; + } else if (constraints.maxWidth <= 1200 && + constraints.maxWidth >= 800) { + return mediumScreen ?? largeScreen; + } else { + return smallScreen ?? largeScreen; + } + }, + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 09ae8cdc..4afa0337 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,49 +7,70 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety.1" + version: "2.5.0" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" collection: dependency: "direct main" description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.3" + version: "1.15.0" + convert: + dependency: transitive + description: + name: convert + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.1" + crypto: + dependency: transitive + description: + name: crypto + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" + csslib: + dependency: transitive + description: + name: csslib + url: "https://pub.dartlang.org" + source: hosted + version: "0.16.2" fake_async: dependency: transitive description: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" flutter: dependency: "direct main" description: flutter @@ -79,6 +100,13 @@ packages: description: flutter source: sdk version: "0.0.0" + html: + dependency: transitive + description: + name: html + url: "https://pub.dartlang.org" + source: hosted + version: "0.14.0+4" http: dependency: transitive description: @@ -107,27 +135,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.1" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.3" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety.1" + version: "0.12.10" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0" path: dependency: transitive description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.1" + version: "1.8.0" pedantic: dependency: transitive description: @@ -181,42 +216,42 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.2" + version: "1.8.0" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.1" + version: "1.10.0" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety.2" + version: "0.2.19" tuple: dependency: "direct main" description: @@ -230,7 +265,21 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0" + universal_html: + dependency: "direct main" + description: + name: universal_html + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.4" + universal_io: + dependency: transitive + description: + name: universal_io + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" url_launcher: dependency: "direct main" description: @@ -279,7 +328,14 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0" + zone_local: + dependency: transitive + description: + name: zone_local + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.2" sdks: - dart: ">=2.10.0-110 <2.11.0" - flutter: ">=1.22.0 <2.0.0" + dart: ">=2.12.0-0.0 <3.0.0" + flutter: ">=1.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index fb373ad1..d9cff81e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -20,6 +20,7 @@ dependencies: flutter_colorpicker: ^0.3.4 image_picker: ^0.6.7+17 photo_view: ^0.10.3 + universal_html: ^1.2.1 dev_dependencies: flutter_test: