Irbisa · Шпаргалка
Внутреннее устройство Flutter
Три дерева, RenderObject, дерево Element, BuildOwner, конвейер рендеринга
Что такое bindings во Flutter (
WidgetsFlutterBinding,SchedulerBinding,RendererBinding,GestureBinding) и за что отвечает каждый?Объясните три дерева Flutter: Widget, Element, RenderObject. Зачем три дерева, а не одно?
Как Flutter рисует интерфейс без нативных OEM-виджетов и что делают фреймворк, движок и embedder?
При переключении свитча ниже
Counterкаждый раз сбрасывается в ноль. Почему он теряетStateи как его сохранить?@overrideWidget build(BuildContext context) { const counter = Counter(); // StatefulWidget that keeps its own count return Column( children: [ Switch( value: _padded, onChanged: (v) => setState(() => _padded = v), ), _padded ? const Padding(padding: EdgeInsets.all(16), child: counter) : counter, ], );}Какой жизненный цикл у Element (mount, update, deactivate, activate, unmount) и когда на самом деле вызывается
State.dispose?Как работает пайплайн отрисовки во Flutter и что происходит между
setStateи пикселями на экране?void main() { WidgetsFlutterBinding.ensureInitialized(); // Real frame timings from the engine, also in release builds SchedulerBinding.instance.addTimingsCallback((timings) { for (final t in timings) { if (t.totalSpan > const Duration(milliseconds: 16)) {…Чем отличаются transient-, persistent- и post-frame-колбэки и когда использовать
addPostFrameCallback?class _HeaderState extends State<Header> { final _key = GlobalKey(); double? _height; @override void initState() {…Оборачивание ребёнка
ColumnвPositionedбросает "Incorrect use of ParentDataWidget". Что на самом деле пытался сделатьPositioned?class TrayParentData extends ContainerBoxParentData<RenderBox> { double weight = 1;} class Weighted extends ParentDataWidget<TrayParentData> { const Weighted({super.key, required this.weight, required super.child});…Этот сеттер вызывается при каждом ребилде через
updateRenderObject, и в таймлайне layout идёт каждый кадр. Что с ним не так?class RenderBadge extends RenderBox { RenderBadge({required Color color}) : _color = color; Color _color; set color(Color value) { _color = value; markNeedsLayout(); } @override void performLayout() => size = constraints.constrain(const Size.square(24)); @override void paint(PaintingContext context, Offset offset) => context.canvas .drawCircle(offset + size.center(Offset.zero), 12, Paint()..color = _color);}Найдите баг:
StatusDotсохраняет первый цвет, даже когда родитель перестраивает его с новым.class StatusDot extends LeafRenderObjectWidget { const StatusDot({super.key, required this.color}); final Color color; @override RenderStatusDot createRenderObject(BuildContext context) => RenderStatusDot(color);}class RenderStatusDot extends RenderBox { RenderStatusDot(this._color); Color _color; set color(Color v) { if (v == _color) return; _color = v; markNeedsPaint(); } @override void performLayout() => size = constraints.constrain(const Size.square(12)); @override void paint(PaintingContext context, Offset offset) => context.canvas.drawCircle(offset + size.center(Offset.zero), 6, Paint()..color = _color);}Как работает hit testing во Flutter и почему ребёнок, вылезающий за границы родителя, не нажимается?
// A round button: hits in the square corners fall through to whatever is behindclass RenderRoundButton extends RenderProxyBox { @override bool hitTest(BoxHitTestResult result, {required Offset position}) { final center = size.center(Offset.zero); if ((position - center).distance > size.shortestSide / 2) return false;…Вы открыли непрозрачный маршрут поверх страницы, где работает
AnimationController. Продолжает ли он тикать под ним?@overridevoid initState() { super.initState(); // wrong: if an opaque route covers this page first, the future waits // until the user comes back, because the ticker is muted, not finished // _controller.forward().then((_) => _startTimer());…Для чего нужен
debugFillPropertiesи как сделать, чтобы свой виджет показывал свои поля во Flutter Inspector?class PriceTag extends StatelessWidget { const PriceTag({super.key, required this.amount, this.currency = 'USD', this.onSale = false}); final double amount; final String currency; final bool onSale;…Почему Flutter может позволить себе перестраивать тысячи виджетов за кадр и когда это перестаёт быть правдой?
AnimatedBuilder( animation: _controller, // The same child instance comes back on every tick, so updateChild // sees identical(old, new) and skips the whole chart child: const ExpensiveChart(), builder: (context, child) => Transform.rotate(…Родитель и один из его потомков оба вызвали
setStateдо одного и того же кадра. Сколько раз выполнится build потомка?При переходе между маршрутами падает "Duplicate GlobalKeys detected in widget tree". Что на самом деле делает
GlobalKeyпод капотом?class _GalleryState extends State<Gallery> { // Created once, owned by one State final _playerKey = GlobalKey<VideoPlayerState>(); bool _fullscreen = false; @override…Что такое
BuildOwnerиPipelineOwnerи зачем работа разделена между ними?Render object вызвал
markNeedsLayout. Как далеко вверх по дереву поднимается эта пометка и что её останавливает?class RenderStripe extends RenderBox with RenderObjectWithChildMixin<RenderBox> { RenderStripe(this._thickness); double _thickness; set thickness(double v) { if (v == _thickness) return;…Найдите баг: после изменения текста внутри
RenderCaptionразмер не обновляется, а в debug сыплется assert.class RenderCaption extends RenderShiftedBox { RenderCaption() : super(null); static const double padding = 8; @override void performLayout() { final child = this.child!; child.layout(constraints.deflate(const EdgeInsets.all(padding))); (child.parentData! as BoxParentData).offset = const Offset(padding, padding); size = constraints.constrain( Size(child.size.width + padding * 2, child.size.height + padding * 2), ); }}Как работает композиция (compositing) во Flutter и когда render object получает собственный слой?
Почему builder у
LayoutBuilderне может вызватьsetStateу предка и как он вообще строит виджеты посреди раскладки?// wrong: the ancestor's build is over, layout is running right nowLayoutBuilder(builder: (context, constraints) { setState(() => _width = constraints.maxWidth); // asserts return Content(width: _width);});…Как написать собственный
RenderBoxи когда это правильный инструмент вместо композиции виджетов илиCustomPainter?class Inset extends SingleChildRenderObjectWidget { const Inset({super.key, required this.gap, super.child}); final double gap; @override RenderObject createRenderObject(BuildContext context) => RenderInset(gap); @override…