ux: bulk WIP — UxPlugin→XPlugin rename + new anim/core/navi/reactive packages
Catch-all commit for outstanding pre-existing local changes. Mixes several themes that would normally be split: - Rename: UxPlugin → XPlugin across iOS, macOS, Android registrants. - New top-level packages under lib/src/: anim/ (animated values, panes, sheets, dock, measured), core/ (Emitter, ReactiveBuilder scaffolding, presenter/widget/value/dispose primitives), navi/ (Screen/ScreenStack/Router/hero/transitions), reactive/. - Edits across existing plugins (clipboard, crash, file, gallery, keyboard, scanner, sensor, url) to align with the new core. - Test updates and CHANGELOG/README touches accompanying the above.
This commit is contained in:
384
lib/src/anim/pane.dart
Normal file
384
lib/src/anim/pane.dart
Normal file
@@ -0,0 +1,384 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:ux/src/core/core.dart';
|
||||
|
||||
/// WIP
|
||||
abstract class Pane with Emitter {
|
||||
Pane();
|
||||
|
||||
final Map<Object?, Widget> widgets = {};
|
||||
final Map<Object?, Element> elements = {};
|
||||
final Map<Object?, RenderBox> renderBoxes = {};
|
||||
|
||||
// ready to manage children
|
||||
PaneElement? element;
|
||||
PaneRender? renderer;
|
||||
|
||||
bool get needsCompositing => renderer?.needsCompositing ?? false;
|
||||
|
||||
Size get size => renderer!.size;
|
||||
|
||||
BoxConstraints get constraints => renderer!.constraints;
|
||||
|
||||
void markNeedsLayout() => renderer?.markNeedsLayout();
|
||||
|
||||
void markNeedsPaint() => renderer?.markNeedsPaint();
|
||||
|
||||
void attachPipeline(PipelineOwner owner) {
|
||||
for (final child in renderBoxes.values) {
|
||||
if (child.owner == null) {
|
||||
child.attach(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void detachPipeline() {
|
||||
for (final child in renderBoxes.values) {
|
||||
child.detach();
|
||||
}
|
||||
}
|
||||
|
||||
void visitChildrenRender(RenderObjectVisitor visitor) =>
|
||||
renderBoxes.values.forEach(visitor);
|
||||
|
||||
void setupParentData(covariant RenderObject child, covariant Object? slot);
|
||||
|
||||
List<DiagnosticsNode> debugDescribeChildren() => const [];
|
||||
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {}
|
||||
|
||||
void applyPaintTransform(covariant RenderObject child, Matrix4 transform) {}
|
||||
|
||||
bool hitTestChildren(BoxHitTestResult result, Offset position) => false;
|
||||
|
||||
bool hitTestSelf(Offset position) => false;
|
||||
|
||||
/// [constraints] are available
|
||||
/// [size] must be set
|
||||
void performLayout();
|
||||
|
||||
void paint(PaintingContext context, Offset offset);
|
||||
|
||||
void insertRenderObjectChild(
|
||||
PaneElement paneElement, covariant RenderBox child, Object? slot) {
|
||||
setupParentData(child, slot);
|
||||
renderBoxes[slot] = child;
|
||||
renderer?.adoptChild(child);
|
||||
}
|
||||
|
||||
void moveRenderObjectChild(PaneElement paneElement, RenderObject child,
|
||||
Object? oldSlot, Object? newSlot) {}
|
||||
|
||||
void removeRenderObjectChild(
|
||||
PaneElement paneElement, RenderObject child, Object? slot) {
|
||||
renderBoxes.remove(slot);
|
||||
renderer?.dropChild(child);
|
||||
}
|
||||
|
||||
void visitChildrenElement(ElementVisitor visitor) =>
|
||||
elements.values.forEach(visitor);
|
||||
|
||||
Widget itemBuilder(BuildContext context, Object? slot);
|
||||
|
||||
RenderBox? invokeLayoutUpsert(Object? slot) {
|
||||
if (element == null) return null;
|
||||
RenderBox? child = renderBoxes[slot];
|
||||
if (child != null) return child;
|
||||
renderer?.invokeLayoutCallback((constraints) {
|
||||
child = buildChild(slot);
|
||||
});
|
||||
return child;
|
||||
}
|
||||
|
||||
void invokeLayoutRemove(Object? slot) {
|
||||
if (element == null) return;
|
||||
renderer?.invokeLayoutCallback((constraints) {
|
||||
removeSlotElement(slot);
|
||||
});
|
||||
}
|
||||
|
||||
void removeSlotElement(Object? slot) {
|
||||
final element = this.element;
|
||||
if (element == null) return;
|
||||
elements.remove(slot)?.pipe(element.deactivateChild);
|
||||
widgets.remove(slot);
|
||||
}
|
||||
|
||||
void mountElement(Element? parent, Object? newSlot, PaneElement element) {
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
void unmountElement() {
|
||||
for (final child in renderBoxes.values) {
|
||||
renderer?.dropChild(child);
|
||||
}
|
||||
renderBoxes.clear();
|
||||
elements.clear();
|
||||
widgets.clear();
|
||||
|
||||
element = null;
|
||||
}
|
||||
|
||||
RenderBox? buildChild(Object? slot) {
|
||||
final element = this.element;
|
||||
if (element == null) return null;
|
||||
|
||||
element.owner!.buildScope(element, () {
|
||||
Widget newWidget;
|
||||
try {
|
||||
newWidget = itemBuilder(element, slot);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
final oldElement = elements[slot];
|
||||
final oldWidget = widgets[slot];
|
||||
if (oldElement == null) {
|
||||
final newElement = element.inflateWidget(newWidget, slot);
|
||||
elements[slot] = newElement;
|
||||
widgets[slot] = newWidget;
|
||||
} else if (oldWidget != null && Widget.canUpdate(oldWidget, newWidget)) {
|
||||
oldElement.update(newWidget);
|
||||
widgets[slot] = newWidget;
|
||||
} else {
|
||||
element.deactivateChild(oldElement);
|
||||
final newElement = element.inflateWidget(newWidget, slot);
|
||||
elements[slot] = newElement;
|
||||
widgets[slot] = newWidget;
|
||||
}
|
||||
});
|
||||
return elements[slot]?.renderObject as RenderBox?;
|
||||
}
|
||||
|
||||
void forgetChildElement(Element child) {
|
||||
elements.removeWhere((_, value) => value == child);
|
||||
}
|
||||
}
|
||||
|
||||
class PaneView extends StatelessWidget {
|
||||
const PaneView({
|
||||
required this.controller,
|
||||
required this.scrollController,
|
||||
this.scrollPhysics,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Pane controller;
|
||||
final ScrollController scrollController;
|
||||
final ScrollPhysics? scrollPhysics;
|
||||
|
||||
Widget buildViewport(BuildContext context, ViewportOffset offset) {
|
||||
return PaneViewport(controller: controller);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scrollable(
|
||||
controller: scrollController,
|
||||
physics: scrollPhysics,
|
||||
viewportBuilder: buildViewport,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PaneViewport extends RenderObjectWidget {
|
||||
const PaneViewport({
|
||||
required this.controller,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Pane controller;
|
||||
|
||||
@override
|
||||
RenderObjectElement createElement() {
|
||||
return PaneElement(this, controller);
|
||||
}
|
||||
|
||||
@override
|
||||
RenderObject createRenderObject(BuildContext context) {
|
||||
return PaneRender(controller: controller);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateRenderObject(
|
||||
BuildContext context, covariant PaneRender renderObject) {
|
||||
renderObject.controller = controller;
|
||||
}
|
||||
}
|
||||
|
||||
class PaneElement extends RenderObjectElement {
|
||||
PaneElement(super.widget, this._controller);
|
||||
|
||||
Pane _controller;
|
||||
|
||||
Pane get controller => _controller;
|
||||
|
||||
@override
|
||||
void insertRenderObjectChild(
|
||||
covariant RenderBox child, covariant Object? slot) {
|
||||
controller.insertRenderObjectChild(this, child, slot);
|
||||
}
|
||||
|
||||
@override
|
||||
void moveRenderObjectChild(covariant RenderObject child,
|
||||
covariant Object? oldSlot, covariant Object? newSlot) {
|
||||
controller.moveRenderObjectChild(this, child, oldSlot, newSlot);
|
||||
}
|
||||
|
||||
@override
|
||||
void removeRenderObjectChild(
|
||||
covariant RenderObject child, covariant Object? slot) {
|
||||
controller.removeRenderObjectChild(this, child, slot);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitChildren(ElementVisitor visitor) =>
|
||||
controller.visitChildrenElement(visitor);
|
||||
|
||||
@override
|
||||
void deactivateChild(Element child) => super.deactivateChild(child);
|
||||
|
||||
@override
|
||||
void mount(Element? parent, Object? newSlot) {
|
||||
super.mount(parent, newSlot);
|
||||
controller.mountElement(parent, newSlot, this);
|
||||
}
|
||||
|
||||
@override
|
||||
void unmount() {
|
||||
controller.unmountElement();
|
||||
super.unmount();
|
||||
}
|
||||
|
||||
@override
|
||||
void update(covariant PaneViewport newWidget) {
|
||||
final oldController = _controller;
|
||||
final newController = newWidget.controller;
|
||||
if (oldController != newController) {
|
||||
oldController.unmountElement();
|
||||
_controller = newController;
|
||||
newController.mountElement(null, null, this);
|
||||
}
|
||||
super.update(newWidget);
|
||||
}
|
||||
|
||||
@override
|
||||
void forgetChild(Element child) {
|
||||
controller.forgetChildElement(child);
|
||||
super.forgetChild(child);
|
||||
}
|
||||
|
||||
@override
|
||||
Element inflateWidget(Widget newWidget, Object? newSlot) =>
|
||||
super.inflateWidget(newWidget, newSlot);
|
||||
|
||||
@override
|
||||
PaneRender get renderObject => super.renderObject as PaneRender;
|
||||
|
||||
@override
|
||||
PaneViewport get widget => super.widget as PaneViewport;
|
||||
}
|
||||
|
||||
class PaneRender extends RenderBox {
|
||||
PaneRender({
|
||||
required Pane controller,
|
||||
}) : _controller = controller;
|
||||
|
||||
Pane _controller;
|
||||
|
||||
Pane get controller => _controller;
|
||||
|
||||
set controller(Pane value) {
|
||||
if (_controller == value) return;
|
||||
if (attached) {
|
||||
_controller.detachPipeline();
|
||||
_controller.renderer = null;
|
||||
}
|
||||
_controller = value;
|
||||
if (attached) {
|
||||
_controller.attachPipeline(owner!);
|
||||
_controller.renderer = this;
|
||||
markNeedsLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void attach(PipelineOwner owner) {
|
||||
super.attach(owner);
|
||||
_controller.attachPipeline(owner);
|
||||
_controller.renderer = this;
|
||||
}
|
||||
|
||||
@override
|
||||
void detach() {
|
||||
super.detach();
|
||||
_controller.detachPipeline();
|
||||
_controller.renderer = null;
|
||||
}
|
||||
|
||||
@override
|
||||
void adoptChild(RenderObject child) => super.adoptChild(child);
|
||||
|
||||
@override
|
||||
void dropChild(RenderObject child) => super.dropChild(child);
|
||||
|
||||
@override
|
||||
void setupParentData(covariant RenderObject child) {
|
||||
//no-op
|
||||
}
|
||||
|
||||
@override
|
||||
bool get sizedByParent => true;
|
||||
|
||||
@override
|
||||
void performResize() {
|
||||
size = constraints.biggest;
|
||||
}
|
||||
|
||||
@override
|
||||
void performLayout() {
|
||||
if (controller.renderer == null) return;
|
||||
controller.performLayout();
|
||||
}
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
if (controller.renderer == null) return;
|
||||
controller.paint(context, offset);
|
||||
}
|
||||
|
||||
@override
|
||||
bool hitTestSelf(Offset position) => controller.hitTestSelf(position);
|
||||
|
||||
@override
|
||||
void invokeLayoutCallback<T extends Constraints>(
|
||||
LayoutCallback<T> callback) =>
|
||||
super.invokeLayoutCallback(callback);
|
||||
|
||||
@override
|
||||
bool hitTestChildren(
|
||||
BoxHitTestResult result, {
|
||||
required Offset position,
|
||||
}) =>
|
||||
controller.hitTestChildren(result, position);
|
||||
|
||||
@override
|
||||
void visitChildren(RenderObjectVisitor visitor) =>
|
||||
controller.visitChildrenRender(visitor);
|
||||
|
||||
@override
|
||||
void applyPaintTransform(RenderObject child, Matrix4 transform) {
|
||||
assert(child.parent == this);
|
||||
controller.applyPaintTransform(child, transform);
|
||||
}
|
||||
|
||||
@override
|
||||
List<DiagnosticsNode> debugDescribeChildren() =>
|
||||
controller.debugDescribeChildren();
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
controller.debugFillProperties(properties);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user