Step 7 of remaking the example

pull/1530/head
Ellet 1 year ago
parent 7f1b4bd699
commit ec44d81f7c
No known key found for this signature in database
GPG Key ID: C488CC70BBCEF0D1
  1. 45
      example/lib/presentation/quill/embeds/timestamp_embed.dart
  2. 26
      example/lib/presentation/quill/quill_editor.dart
  3. 61
      example/lib/presentation/quill/quill_toolbar.dart
  4. 15
      example/macos/Podfile.lock
  5. 2
      example/macos/Runner/DebugProfile.entitlements
  6. 2
      example/macos/Runner/Release.entitlements
  7. 2
      flutter_quill_extensions/lib/presentation/embeds/editor/image/image_menu.dart
  8. 16
      flutter_quill_extensions/lib/presentation/utils/utils.dart
  9. 6
      lib/src/utils/platform.dart

@ -0,0 +1,45 @@
import 'dart:convert' show jsonDecode, jsonEncode;
import 'package:flutter/material.dart' show Icons;
import 'package:flutter/widgets.dart';
import 'package:flutter_quill/flutter_quill.dart';
class TimeStampEmbed extends Embeddable {
const TimeStampEmbed(
String value,
) : super(timeStampType, value);
static const String timeStampType = 'timeStamp';
static TimeStampEmbed fromDocument(Document document) =>
TimeStampEmbed(jsonEncode(document.toDelta().toJson()));
Document get document => Document.fromJson(jsonDecode(data));
}
class TimeStampEmbedBuilderWidget extends EmbedBuilder {
@override
String get key => 'timeStamp';
@override
String toPlainText(Embed node) {
return node.value.data;
}
@override
Widget build(
BuildContext context,
QuillController controller,
Embed node,
bool readOnly,
bool inline,
TextStyle textStyle,
) {
return Row(
children: [
const Icon(Icons.access_time_rounded),
Text(node.value.data as String),
],
);
}
}

@ -1,3 +1,5 @@
import 'dart:io' as io show Directory, File;
import 'package:cached_network_image/cached_network_image.dart' import 'package:cached_network_image/cached_network_image.dart'
show CachedNetworkImageProvider; show CachedNetworkImageProvider;
import 'package:desktop_drop/desktop_drop.dart' show DropTarget; import 'package:desktop_drop/desktop_drop.dart' show DropTarget;
@ -7,8 +9,10 @@ import 'package:flutter_quill/flutter_quill.dart';
import 'package:flutter_quill_extensions/flutter_quill_extensions.dart'; import 'package:flutter_quill_extensions/flutter_quill_extensions.dart';
import 'package:flutter_quill_extensions/presentation/embeds/widgets/image.dart' import 'package:flutter_quill_extensions/presentation/embeds/widgets/image.dart'
show getImageProviderByImageSource, imageFileExtensions; show getImageProviderByImageSource, imageFileExtensions;
import 'package:path/path.dart' as path;
import '../extensions/scaffold_messenger.dart'; import '../extensions/scaffold_messenger.dart';
import 'embeds/timestamp_embed.dart';
class MyQuillEditor extends StatelessWidget { class MyQuillEditor extends StatelessWidget {
const MyQuillEditor({ const MyQuillEditor({
@ -31,7 +35,23 @@ class MyQuillEditor extends StatelessWidget {
scrollable: true, scrollable: true,
placeholder: 'Start writting your notes...', placeholder: 'Start writting your notes...',
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
embedBuilders: isWeb() onImagePaste: (imageBytes) async {
if (isWeb()) {
return null;
}
// We will save it to system temporary files
final newFileName = '${DateTime.now().toIso8601String()}.png';
final newPath = path.join(
io.Directory.systemTemp.path,
newFileName,
);
final file = await io.File(
newPath,
).writeAsBytes(imageBytes, flush: true);
return file.path;
},
embedBuilders: [
...(isWeb()
? FlutterQuillEmbeds.editorWebBuilders() ? FlutterQuillEmbeds.editorWebBuilders()
: FlutterQuillEmbeds.editorBuilders( : FlutterQuillEmbeds.editorBuilders(
imageEmbedConfigurations: QuillEditorImageEmbedConfigurations( imageEmbedConfigurations: QuillEditorImageEmbedConfigurations(
@ -63,7 +83,9 @@ class MyQuillEditor extends StatelessWidget {
); );
}, },
), ),
), )),
TimeStampEmbedBuilderWidget(),
],
builder: (context, rawEditor) { builder: (context, rawEditor) {
// The `desktop_drop` plugin doesn't support iOS platform for now // The `desktop_drop` plugin doesn't support iOS platform for now
if (isIOS(supportWeb: false)) { if (isIOS(supportWeb: false)) {

@ -12,6 +12,7 @@ import 'package:path_provider/path_provider.dart'
import '../extensions/scaffold_messenger.dart'; import '../extensions/scaffold_messenger.dart';
import '../settings/cubit/settings_cubit.dart'; import '../settings/cubit/settings_cubit.dart';
import 'embeds/timestamp_embed.dart';
class MyQuillToolbar extends StatelessWidget { class MyQuillToolbar extends StatelessWidget {
const MyQuillToolbar({super.key}); const MyQuillToolbar({super.key});
@ -67,13 +68,19 @@ class MyQuillToolbar extends StatelessWidget {
controller.insertImageBlock(imageSource: newSavedImage); controller.insertImageBlock(imageSource: newSavedImage);
} }
/// Copies the picked file from temporary cache to applications directory /// For mobile platforms it will copies the picked file from temporary cache
/// to applications directory
///
/// for desktop platforms, it will do the same but from user files this time
Future<String> saveImage(io.File file) async { Future<String> saveImage(io.File file) async {
final appDocDir = await getApplicationDocumentsDirectory(); final appDocDir = await getApplicationDocumentsDirectory();
final copiedFile = await file.copy(path.join( final fileExt = path.extension(file.path);
final newFileName = '${DateTime.now().toIso8601String()}$fileExt';
final newPath = path.join(
appDocDir.path, appDocDir.path,
'${DateTime.now().toIso8601String()}${path.extension(file.path)}', newFileName,
)); );
final copiedFile = await file.copy(newPath);
return copiedFile.path; return copiedFile.path;
} }
@ -205,6 +212,52 @@ class MyQuillToolbar extends StatelessWidget {
return QuillToolbar( return QuillToolbar(
configurations: QuillToolbarConfigurations( configurations: QuillToolbarConfigurations(
customButtons: [ customButtons: [
QuillToolbarCustomButtonOptions(
icon: const Icon(Icons.add_alarm_rounded),
onPressed: () {
final controller = context.requireQuillController;
controller.document
.insert(controller.selection.extentOffset, '\n');
controller.updateSelection(
TextSelection.collapsed(
offset: controller.selection.extentOffset + 1,
),
ChangeSource.local,
);
controller.document.insert(
controller.selection.extentOffset,
TimeStampEmbed(
DateTime.now().toString(),
),
);
controller.updateSelection(
TextSelection.collapsed(
offset: controller.selection.extentOffset + 1,
),
ChangeSource.local,
);
controller.document
.insert(controller.selection.extentOffset, ' ');
controller.updateSelection(
TextSelection.collapsed(
offset: controller.selection.extentOffset + 1,
),
ChangeSource.local,
);
controller.document
.insert(controller.selection.extentOffset, '\n');
controller.updateSelection(
TextSelection.collapsed(
offset: controller.selection.extentOffset + 1,
),
ChangeSource.local,
);
},
),
QuillToolbarCustomButtonOptions( QuillToolbarCustomButtonOptions(
icon: const Icon(Icons.ac_unit), icon: const Icon(Icons.ac_unit),
onPressed: () { onPressed: () {

@ -6,6 +6,9 @@ PODS:
- file_selector_macos (0.0.1): - file_selector_macos (0.0.1):
- FlutterMacOS - FlutterMacOS
- FlutterMacOS (1.0.0) - FlutterMacOS (1.0.0)
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
- gal (1.0.0): - gal (1.0.0):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
@ -16,6 +19,9 @@ PODS:
- FlutterMacOS - FlutterMacOS
- share_plus (0.0.1): - share_plus (0.0.1):
- FlutterMacOS - FlutterMacOS
- sqflite (0.0.2):
- FlutterMacOS
- FMDB (>= 2.7.5)
- url_launcher_macos (0.0.1): - url_launcher_macos (0.0.1):
- FlutterMacOS - FlutterMacOS
- video_player_avfoundation (0.0.1): - video_player_avfoundation (0.0.1):
@ -31,9 +37,14 @@ DEPENDENCIES:
- pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`) - pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
- sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/macos`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
- video_player_avfoundation (from `Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin`) - video_player_avfoundation (from `Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin`)
SPEC REPOS:
trunk:
- FMDB
EXTERNAL SOURCES: EXTERNAL SOURCES:
desktop_drop: desktop_drop:
:path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos :path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos
@ -51,6 +62,8 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
share_plus: share_plus:
:path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos
sqflite:
:path: Flutter/ephemeral/.symlinks/plugins/sqflite/macos
url_launcher_macos: url_launcher_macos:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
video_player_avfoundation: video_player_avfoundation:
@ -61,10 +74,12 @@ SPEC CHECKSUMS:
device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f
file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9 file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
gal: 61e868295d28fe67ffa297fae6dacebf56fd53e1 gal: 61e868295d28fe67ffa297fae6dacebf56fd53e1
pasteboard: 9b69dba6fedbb04866be632205d532fe2f6b1d99 pasteboard: 9b69dba6fedbb04866be632205d532fe2f6b1d99
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7
sqflite: a5789cceda41d54d23f31d6de539d65bb14100ea
url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
video_player_avfoundation: 8563f13d8fc8b2c29dc2d09e60b660e4e8128837 video_player_avfoundation: 8563f13d8fc8b2c29dc2d09e60b660e4e8128837

@ -12,5 +12,7 @@
<true/> <true/>
<key>com.apple.security.files.user-selected.read-only</key> <key>com.apple.security.files.user-selected.read-only</key>
<true/> <true/>
<!-- <key>com.apple.security.files.user-selected.read-write</key>
<true/> -->
</dict> </dict>
</plist> </plist>

@ -8,5 +8,7 @@
<true/> <true/>
<key>com.apple.security.files.user-selected.read-only</key> <key>com.apple.security.files.user-selected.read-only</key>
<true/> <true/>
<!-- <key>com.apple.security.files.user-selected.read-write</key>
<true/> -->
</dict> </dict>
</plist> </plist>

@ -142,7 +142,7 @@ class ImageOptionsMenu extends StatelessWidget {
imageUrl: imageSource, imageUrl: imageSource,
imageSaverService: imageSaverService, imageSaverService: imageSaverService,
); );
final imageSavedSuccessfully = saveImageResult.isSuccess; final imageSavedSuccessfully = saveImageResult.error == null;
messenger.clearSnackBars(); messenger.clearSnackBars();

@ -45,9 +45,9 @@ enum SaveImageResultMethod { network, localStorage }
@immutable @immutable
class SaveImageResult { class SaveImageResult {
const SaveImageResult({required this.isSuccess, required this.method}); const SaveImageResult({required this.error, required this.method});
final bool isSuccess; final String? error;
final SaveImageResultMethod method; final SaveImageResultMethod method;
} }
@ -67,12 +67,12 @@ Future<SaveImageResult> saveImage({
Uri.parse(appendFileExtensionToImageUrl(imageUrl)), Uri.parse(appendFileExtensionToImageUrl(imageUrl)),
); );
return const SaveImageResult( return const SaveImageResult(
isSuccess: true, error: null,
method: SaveImageResultMethod.network, method: SaveImageResultMethod.network,
); );
} catch (e) { } catch (e) {
return const SaveImageResult( return SaveImageResult(
isSuccess: false, error: e.toString(),
method: SaveImageResultMethod.network, method: SaveImageResultMethod.network,
); );
} }
@ -80,12 +80,12 @@ Future<SaveImageResult> saveImage({
try { try {
await imageSaverService.saveLocalImage(imageUrl); await imageSaverService.saveLocalImage(imageUrl);
return const SaveImageResult( return const SaveImageResult(
isSuccess: true, error: null,
method: SaveImageResultMethod.localStorage, method: SaveImageResultMethod.localStorage,
); );
} catch (e) { } catch (e) {
return const SaveImageResult( return SaveImageResult(
isSuccess: false, error: e.toString(),
method: SaveImageResultMethod.localStorage, method: SaveImageResultMethod.localStorage,
); );
} }

@ -2,10 +2,12 @@ import 'dart:io' show Platform;
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart' import 'package:flutter/foundation.dart'
show kIsWeb, TargetPlatform, defaultTargetPlatform; show TargetPlatform, defaultTargetPlatform, kIsWeb, visibleForTesting;
/// If you want to override the [kIsWeb] use [overrideIsWeb] /// If you want to override the [kIsWeb] use [overrideIsWeb]
bool isWeb({bool? overrideIsWeb}) { bool isWeb({
@visibleForTesting bool? overrideIsWeb,
}) {
return overrideIsWeb ?? kIsWeb; return overrideIsWeb ?? kIsWeb;
} }

Loading…
Cancel
Save