AppFlowy-IO / AppFlowy-IO/appflowy-editor

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

Abierto
#1,003 2 comentarios 0 reacciones 0 asignados Ver en GitHub
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
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,
),
),
]),
)));
}
}

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.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.