AppFlowy-IO / AppFlowy-IO/appflowy-editor
[Bug] Cannot capture AppFlowy Editor widget including images inserted as base64 encoded string
- Lenguaje dominante
- Dart
- Estrellas
- 684
- Forks
- 329
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Descripción
### 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
Captured Image
### 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,
),
),
]),
)));
}
}
Guía de contribución
No hay ninguna guía de contribución indexada para este repositorio
Evaluación
Este issue todavía no se ha evaluado.