AppFlowy-IO / AppFlowy-IO/appflowy-editor

[Bug] Cannot capture AppFlowy Editor widget including images inserted as base64 encoded string

Aperta
#1,003 2 commenti 0 reazioni 0 assegnatari Vedi su GitHub
Lingua principale
Dart
Stelle
684
Fork
329
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Descrizione

### Bug Description

I want to export the image file from the current editor content. So I used Screenshot package to capture it. It’s no issue to capture a visible widget. But there is an issue only to capture an invisible widget. I have to capture an invisible widget because my contents are over the screen. The result shows the base64-encoded images to [X] mark.
If I insert images as an url or a local path, no issue for same case. It’s a problem just only for base64 encoded data.

### How to Reproduce

1. Insert an image as the base64 encoded in an editor.
2. Capture from AppFlowy Editor widget with the current document.

### Expected Behavior

Capture image show the same contents with the current editor.

### Operating System

iOS, Android

### AppFlowy Editor Version(s)

4.0.0

### Screenshots

Current AppFlowy Editor Contents
Screenshot 2024-12-26 at 4 40 07 PM

Captured Image
Screenshot 2024-12-26 at 4 39 45 PM

### Additional Context

`appflowy_editor_widget.dart`

import 'package:feasibility_markdown/appflowy_editor/save_image_page.dart';
import 'package:flutter/material.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
import 'dart:convert';

class AppFlowyEditorWidget extends StatefulWidget {
const AppFlowyEditorWidget({super.key});

@override
State createState() => _AppFlowyEditorWidgetState();
}

class _AppFlowyEditorWidgetState extends State {
final _scaffoldKey = GlobalKey();
late EditorState _editorState;
final _localToolbarItems = [
textDecorationMobileToolbarItem,
headingMobileToolbarItem,
todoListMobileToolbarItem,
listMobileToolbarItem,
linkMobileToolbarItem,
quoteMobileToolbarItem,
codeMobileToolbarItem,
blocksMobileToolbarItem,
textDecorationMobileToolbarItemV2
];

@override
void initState() {
super.initState();
_editorState = EditorState.blank();
}

@override
void dispose() {
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('AppFlowy Editor'),
),
body: Column(children: [
Expanded(
child: AppFlowyEditor(
characterShortcutEvents: standardCharacterShortcutEvents,
editorStyle: const EditorStyle.mobile(),
editorState: _editorState)),
Padding(
padding: const EdgeInsets.all(5),
child:
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
InkWell(
child: const Icon(Icons.image),
onTap: () async {
final ImagePicker picker = ImagePicker();
final XFile? pickedImage =
await picker.pickImage(source: ImageSource.gallery);
if (pickedImage != null) {
var imageBytes =
File(pickedImage.path).readAsBytesSync();
_editorState.insertImageNode(base64Encode(imageBytes));
}
}),
InkWell(
child: const Icon(Icons.save_alt),
onTap: () async {
Navigator.of(context).push(MaterialPageRoute(
builder: (_) =>
SaveImagePage(editorState: _editorState)));
}),
])),
MobileToolbar(
editorState: _editorState, toolbarItems: _localToolbarItems)
]));
}
}

`save_image_page.dart`

import 'dart:io';

import 'package:appflowy_editor/appflowy_editor.dart' as appflowy;
import 'package:flutter/material.dart';
import 'package:screenshot/screenshot.dart';
import 'package:gal/gal.dart';

class SaveImagePage extends StatefulWidget {
const SaveImagePage({super.key, required this.editorState});

final appflowy.EditorState editorState;

@override
State createState() => _SaveImagePageState();
}

class _SaveImagePageState extends State {
final _scaffoldKey = GlobalKey();
late ScreenshotController _screenshotController;
late appflowy.EditorState _editorState;

@override
void initState() {
super.initState();
_editorState = appflowy.EditorState(
document:
appflowy.Document.fromJson(widget.editorState.document.toJson()));
_screenshotController = ScreenshotController();

WidgetsBinding.instance.addPostFrameCallback((_) => _saveImage());
}

Widget _captureToSaveToImageWidget() {
const physicalPixelBaseLength = 512.0;
final logicalPixelBaseLength =
physicalPixelBaseLength / MediaQuery.of(context).devicePixelRatio;

return Container(
color: Colors.white,
width: logicalPixelBaseLength,
constraints: BoxConstraints(minHeight: logicalPixelBaseLength),
child: MediaQuery(
data: const MediaQueryData(),
child: IntrinsicHeight(
child: appflowy.AppFlowyEditor(
characterShortcutEvents:
appflowy.standardCharacterShortcutEvents,
editorStyle: const appflowy.EditorStyle.mobile(),
disableKeyboardService: true,
disableSelectionService: true,
shrinkWrap: true,
editorScrollController: appflowy.EditorScrollController(
editorState: _editorState, shrinkWrap: true),
editorState: _editorState),
// )),
)),
);
}

void _saveImage() async {
_screenshotController
.captureFromLongWidget(_captureToSaveToImageWidget(),
pixelRatio: MediaQuery.of(context).devicePixelRatio,
delay: const Duration(seconds: 1))
.then((capturedImage) async {
if (!mounted) return;

final file =
await File('${Directory.systemTemp.path}/flutter_image.png').create();
await file.writeAsBytes(capturedImage);
await Gal.putImage(file.path);

Navigator.of(context).pop();
});
}

@override
void dispose() {
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
backgroundColor: Colors.white,
body: const SafeArea(
child: SizedBox(
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Saving ...",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600,
fontSize: 14.0,
),
),
]),
)));
}
}

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.