Open llfbandit opened 3 weeks ago
We recently switched SuperEditor
to use Slivers. You now need to use a CustomScrollView
to contain a SuperEditor
instead of a SingleChildScrollView
.
Please confirm whether that solves your problem.
Replacing it with this works
CustomScrollView(slivers: [SuperEditor(...)])
But still this doesn't work
CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: SizedBox(
height: 200,
child: SuperEditor(...),
),
),
],
)
Restricting SuperEditor
to only function within CustomScrollView
creates a significant challenge, limiting its flexibility compared to being a standard Widget.
For example how would I do something like this:
import 'package:flutter/material.dart';
import 'package:super_editor/super_editor.dart';
void main() {
runApp(const MainApp());
}
class MainApp extends StatefulWidget {
const MainApp({super.key});
@override
State<MainApp> createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
@override
Widget build(BuildContext context) {
final paragraphs = [
[
ParagraphNode(
id: '1',
text: AttributedText(
'Paragraph 1: Notice that when this document is short enough, the messages are pushed to the bottom of the viewport.\n\nTry adding more content to see things scroll.',
),
)
],
[
ParagraphNode(
id: '2',
text: AttributedText(
'Paragraph 2: Notice that when this document is short enough, the messages are pushed to the bottom of the viewport.\n\nTry adding more content to see things scroll.',
),
)
],
[
ParagraphNode(
id: '3',
text: AttributedText(
'Paragraph 3: Notice that when this document is short enough, the messages are pushed to the bottom of the viewport.\n\nTry adding more content to see things scroll.',
),
)
],
];
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: CustomScrollView(
slivers: [
const SliverAppBar(
title: Text('Super Editor Example'),
floating: true,
snap: true,
),
SliverList.builder(
itemCount: paragraphs.length,
itemBuilder: (context, index) {
return Column(
children: [
Text('Paragraph ${index + 1}'),
NodesRenderer(nodes: paragraphs[index]),
],
);
},
),
],
),
),
),
);
}
}
class NodesRenderer extends StatefulWidget {
final List<DocumentNode> nodes;
const NodesRenderer({super.key, required this.nodes});
@override
State<NodesRenderer> createState() => _NodesRendererState();
}
class _NodesRendererState extends State<NodesRenderer> {
late MutableDocument _doc;
@override
Widget build(BuildContext context) {
return SuperReader(document: _doc);
}
@override
void initState() {
super.initState();
_doc = _createDocument();
}
MutableDocument _createDocument() {
return MutableDocument(nodes: widget.nodes);
}
}
I just finished the upgrade. Finally, I just wrapped the editor with CustomScrollView
for now and added dedicated UI size constaints to recover what it was before.
After a bit of testing, when there's no scrollable ancestor to the editor, so for example, putting it directly in the body of a Scaffold
or at the root of an app, it works. So I guess there's a missing piece here. Since SliverHybridStack
is already inside a CustomScrollView
internally I don't know what's going on. Maybe this is just a context issue when finding scrollable ancestor...
Off-topic: Other than that, so far, 0.3.0-dev.4 is quite stable (I don't use history feature).
The only issue I noticed is with multi-line selection. The view scrolls from the center of the viewport and very quickly. AutoScrollController
?
@paurakhsharma - I'm not sure what you're trying to communicate with the large code sample you provided. Can you describe your UI/UX goal that you're unable to achieve?
@llfbandit - You alluded to two bugs. Can you please make sure you file tickets for those issues if you haven't already?
Based on @knopp's response and explanations here, here is a widget that can help you get back the old behavior if you wrap SuperEditor
with it:
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:super_editor/super_editor.dart';
class SuperEditorUnsliverizer extends StatelessWidget {
final SuperEditor child;
const SuperEditorUnsliverizer({super.key, required this.child});
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
physics: const NeverScrollableScrollPhysics(),
child: _FakeViewport(child: child),
);
}
}
class _FakeViewport extends SingleChildRenderObjectWidget {
const _FakeViewport({super.key, required super.child});
@override
RenderObject createRenderObject(BuildContext context) {
return _RenderFakeViewport();
}
}
class _RenderFakeViewport extends RenderBox
with RenderObjectWithChildMixin<RenderSliver>
implements RenderAbstractViewport {
@override
void debugAssertDoesMeetConstraints() {}
@override
RevealedOffset getOffsetToReveal(RenderObject target, double alignment, {Rect? rect, Axis? axis}) {
return const RevealedOffset(offset: 0, rect: Rect.zero);
}
@override
void setupParentData(RenderObject child) {}
@override
Rect get paintBounds => Rect.zero;
@override
void performLayout() {
final childConstraints = SliverConstraints(
axisDirection: AxisDirection.down,
growthDirection: GrowthDirection.forward,
userScrollDirection: ScrollDirection.forward,
scrollOffset: 0,
precedingScrollExtent: 0,
overlap: 0,
remainingPaintExtent: constraints.maxHeight,
crossAxisExtent: constraints.maxWidth,
crossAxisDirection: AxisDirection.right,
viewportMainAxisExtent: constraints.maxHeight,
remainingCacheExtent: double.infinity,
cacheOrigin: 0,
);
child!.layout(childConstraints, parentUsesSize: true);
final geometry = child!.geometry;
size = Size(constraints.maxWidth, geometry!.scrollExtent);
}
RenderBox _getBox(RenderSliver sliver) {
RenderSliver? firstSliver;
RenderBox? firstBox;
sliver.visitChildren((child) {
if (child is RenderSliver && firstSliver == null) {
firstSliver = child;
}
if (child is RenderBox && firstBox == null) {
firstBox = child;
}
});
return firstSliver != null ? _getBox(firstSliver!) : firstBox!;
}
@override
Size computeDryLayout(covariant BoxConstraints constraints) {
final layoutBox = _getBox(child!);
return layoutBox.computeDryLayout(constraints);
}
@override
double computeMaxIntrinsicWidth(double height) {
final layoutBox = _getBox(child!);
return layoutBox.computeMaxIntrinsicWidth(height);
}
@override
double computeMinIntrinsicWidth(double height) {
final layoutBox = _getBox(child!);
return layoutBox.computeMinIntrinsicWidth(height);
}
@override
computeMaxIntrinsicHeight(double width) {
final layoutBox = _getBox(child!);
return layoutBox.computeMaxIntrinsicHeight(width);
}
@override
computeMinIntrinsicHeight(double width) {
final layoutBox = _getBox(child!);
return layoutBox.computeMinIntrinsicHeight(width);
}
@override
void applyPaintTransform(RenderObject child, Matrix4 transform) {}
@override
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
return child!.hitTest(
SliverHitTestResult.wrap(result),
mainAxisPosition: position.dy,
crossAxisPosition: position.dx,
);
}
@override
void paint(PaintingContext context, Offset offset) {
context.paintChild(child!, offset);
}
@override
void performResize() {}
@override
Rect get semanticBounds => Offset.zero & size;
}
Depending on your needs, you might not need the SingleChildScrollview
or want different physics, etc. That part is pretty hacky and maybe won't be necessary with future versions.
it's already few hours I can't just add this package to the project. Please make Understandable Readme. Because now it's completely not
This bug I'm trying to solve, it's always gives this error of RenderBox
I will try to downgrade now, as it could be recent changes which leaded to the bugs
@andreidevo, can you provide a reproducible example?
As for the original issue, instead of putting SuperEditor into SingleChildScrollView
, you can put it inside CustomScrollView
as a sliver and things will just work. Or don't use a scroll view at all and SuperEditor will create its own.
Package Version 0.3.0-dev.4
0.3.0-dev.2 is OK. The issue has raised since
SliverHybridStack
introduction.I use
SuperEditor
with many other components in screens. This version restricts to use it as main component.Minimal Reproduction Code
Error log and stack trace
════════ Exception caught by widgets library ═══════════════════════════════════ The following assertion was thrown building _FocusInheritedScope: A RenderFlex expected a child of type RenderBox but received a child of type _RenderSliverHybridStack. RenderObjects expect specific types of children because they coordinate with their children during layout and paint. For example, a RenderSliver cannot be the child of a RenderBox because a RenderSliver does not understand the RenderBox layout protocol. The RenderFlex that expected a RenderBox child was created by: Column ← _Decorator ← InputDecorator ← Column ← AppRichTextEditorInternal ← FutureBuilderActual behavior UI is broken.
Expected behavior
SuperEditor
should be insertable anywhere in the treeview.Platform Windows, but could obviously be replicated on any platform.
Flutter version 3.22.3