From 2366102cce093e70bdcb81f68a04afea14186967 Mon Sep 17 00:00:00 2001 From: ruinivist Date: Sun, 27 Jul 2025 00:57:43 +0000 Subject: [PATCH] center init focus logic --- lib/core/controller/controller.dart | 25 +++++++++++++++++++++---- lib/core/controller/types.dart | 2 +- lib/utils/measure_size.dart | 4 ++-- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/core/controller/controller.dart b/lib/core/controller/controller.dart index 1bb610f..db2e9e7 100644 --- a/lib/core/controller/controller.dart +++ b/lib/core/controller/controller.dart @@ -26,6 +26,8 @@ class LazyCanvasController with ChangeNotifier { late final SpatialHashing _spatialHash; TickerProvider? _ticker; late BuildContext _context; + bool _firstBuild = true; + int? _focusChildOnInit; // if set, will focus on this child on first build bool debug; final Duration defaultAnimationDuration; @@ -60,7 +62,7 @@ class LazyCanvasController with ChangeNotifier { _buildExtent = Offset(size.width, size.height) + (_buildCacheExtent ?? Offset(size.width * 0.1, size.height * 0.1)); _canvasSize = size; // allow resize due to canvas resize - // if the first init, re-render as not I have the canvas size to build widgets + // if the first init, re-render as I don't have the canvas size to build widgets if (!_init) { _init = true; Future.microtask(notifyListeners); @@ -84,8 +86,17 @@ class LazyCanvasController with ChangeNotifier { // ==================== Child Management ==================== /// Add a child at a given position with a widget. Returns the child ID. - int addChild(Offset position, Widget widget) { - _children[_nextId] = _ChildInfo(gsPosition: position, widget: widget); + /// You need the child size for optimising the focus on child + int addChild(Offset position, Widget widget, {bool focusOnInit = false, Size? childSize}) { + if (focusOnInit) { + assert( + childSize != null, + 'Child size must be provided when focusing on init (to know positions before any build). Else, you must call focusOnChild in a post frame callback.', + ); + assert(_focusChildOnInit == null, 'Focus child on init is already set. Cannot set it again.'); + _focusChildOnInit = _nextId; + } + _children[_nextId] = _ChildInfo(gsPosition: position, widget: widget, lastRenderedSize: childSize); _spatialHash.add(position.toPoint(), _nextId); // add to spatial hash notifyListeners(); return _nextId++; @@ -187,6 +198,12 @@ class LazyCanvasController with ChangeNotifier { List widgetsWithScreenPositions({bool forceRebuild = false}) { if (!_init) return []; + if (_firstBuild && _focusChildOnInit != null) { + // if this is the first build, focus on the child if set + focusOnChild(_focusChildOnInit!, animate: false); + _firstBuild = false; + } + final idsToBuild = _childrenWithinBuildArea(_gsCenter, _buildExtent); return idsToBuild.map((id) { @@ -246,7 +263,7 @@ class LazyCanvasController with ChangeNotifier { // else do an offstage render childSize ??= childInfo.lastRenderedSize != null && !forceRedraw ? childInfo.lastRenderedSize - : measureWidgetSize(_context, (_) => childInfo.widget); + : measureWidgetSize(_context, childInfo.widget); /* margin is symmatric on ltrb so diff --git a/lib/core/controller/types.dart b/lib/core/controller/types.dart index 475e501..4460b71 100644 --- a/lib/core/controller/types.dart +++ b/lib/core/controller/types.dart @@ -11,7 +11,7 @@ class _ChildInfo { Size? lastRenderedSize; final Widget widget; - _ChildInfo({required this.gsPosition, required this.widget}); + _ChildInfo({required this.gsPosition, required this.widget, this.lastRenderedSize}); } class ChildInfo { diff --git a/lib/utils/measure_size.dart b/lib/utils/measure_size.dart index ea334c1..9853d38 100644 --- a/lib/utils/measure_size.dart +++ b/lib/utils/measure_size.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; /// Utility to synchronously measure the size of a widget by building it offscreen in an Overlay. /// Only works for widgets with deterministic, constraint-based sizing (no async/layout dependencies). -Size measureWidgetSize(BuildContext context, WidgetBuilder builder, {BoxConstraints? constraints}) { +Size measureWidgetSize(BuildContext context, Widget child, {BoxConstraints? constraints}) { final key = GlobalKey(); final overlay = Overlay.of(context); Size? measuredSize; @@ -10,7 +10,7 @@ Size measureWidgetSize(BuildContext context, WidgetBuilder builder, {BoxConstrai final widget = Offstage( child: ConstrainedBox( constraints: constraints ?? const BoxConstraints(), - child: Container(key: key, child: builder(context)), + child: Container(key: key, child: child), ), );