center init focus logic

This commit is contained in:
2025-07-27 00:57:43 +00:00
parent 1fb559f5f0
commit 2366102cce
3 changed files with 24 additions and 7 deletions
+21 -4
View File
@@ -26,6 +26,8 @@ class LazyCanvasController with ChangeNotifier {
late final SpatialHashing<int> _spatialHash; late final SpatialHashing<int> _spatialHash;
TickerProvider? _ticker; TickerProvider? _ticker;
late BuildContext _context; late BuildContext _context;
bool _firstBuild = true;
int? _focusChildOnInit; // if set, will focus on this child on first build
bool debug; bool debug;
final Duration defaultAnimationDuration; 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)); _buildExtent = Offset(size.width, size.height) + (_buildCacheExtent ?? Offset(size.width * 0.1, size.height * 0.1));
_canvasSize = size; // allow resize due to canvas resize _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) { if (!_init) {
_init = true; _init = true;
Future.microtask(notifyListeners); Future.microtask(notifyListeners);
@@ -84,8 +86,17 @@ class LazyCanvasController with ChangeNotifier {
// ==================== Child Management ==================== // ==================== Child Management ====================
/// Add a child at a given position with a widget. Returns the child ID. /// Add a child at a given position with a widget. Returns the child ID.
int addChild(Offset position, Widget widget) { /// You need the child size for optimising the focus on child
_children[_nextId] = _ChildInfo(gsPosition: position, widget: widget); 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 _spatialHash.add(position.toPoint(), _nextId); // add to spatial hash
notifyListeners(); notifyListeners();
return _nextId++; return _nextId++;
@@ -187,6 +198,12 @@ class LazyCanvasController with ChangeNotifier {
List<ChildInfo> widgetsWithScreenPositions({bool forceRebuild = false}) { List<ChildInfo> widgetsWithScreenPositions({bool forceRebuild = false}) {
if (!_init) return []; 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); final idsToBuild = _childrenWithinBuildArea(_gsCenter, _buildExtent);
return idsToBuild.map((id) { return idsToBuild.map((id) {
@@ -246,7 +263,7 @@ class LazyCanvasController with ChangeNotifier {
// else do an offstage render // else do an offstage render
childSize ??= childInfo.lastRenderedSize != null && !forceRedraw childSize ??= childInfo.lastRenderedSize != null && !forceRedraw
? childInfo.lastRenderedSize ? childInfo.lastRenderedSize
: measureWidgetSize(_context, (_) => childInfo.widget); : measureWidgetSize(_context, childInfo.widget);
/* /*
margin is symmatric on ltrb so margin is symmatric on ltrb so
+1 -1
View File
@@ -11,7 +11,7 @@ class _ChildInfo {
Size? lastRenderedSize; Size? lastRenderedSize;
final Widget widget; final Widget widget;
_ChildInfo({required this.gsPosition, required this.widget}); _ChildInfo({required this.gsPosition, required this.widget, this.lastRenderedSize});
} }
class ChildInfo { class ChildInfo {
+2 -2
View File
@@ -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. /// 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). /// 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 key = GlobalKey();
final overlay = Overlay.of(context); final overlay = Overlay.of(context);
Size? measuredSize; Size? measuredSize;
@@ -10,7 +10,7 @@ Size measureWidgetSize(BuildContext context, WidgetBuilder builder, {BoxConstrai
final widget = Offstage( final widget = Offstage(
child: ConstrainedBox( child: ConstrainedBox(
constraints: constraints ?? const BoxConstraints(), constraints: constraints ?? const BoxConstraints(),
child: Container(key: key, child: builder(context)), child: Container(key: key, child: child),
), ),
); );