1+ import 'dart:math' ;
2+
13import 'package:flutter/widgets.dart' ;
24import '../models/figma_frame_model.dart' ;
35import 'figma_frame.dart' ;
6+ import 'stateful_figma_node.dart' ;
47
5- class FigmaSafeArea extends FigmaFrame {
8+ class FigmaSafeArea extends StatefulFigmaNode < FigmaFrameModel > {
69 const FigmaSafeArea ({
710 required super .model,
811 super .key
912 });
1013
1114 static FigmaSafeArea fromJson (Map <String , dynamic > json) {
12- return FigmaFrame .fromJson (json, FigmaSafeArea .new , FigmaFrameModel .fromJson);
15+ return FigmaFrame .fromJson (json, FigmaSafeArea .new );
16+ }
17+
18+ @override
19+ StatefulFigmaNodeState createState () => FigmaSafeAreaState ();
20+ }
21+
22+ class FigmaSafeAreaState extends StatefulFigmaNodeState <FigmaSafeArea >
23+ with SingleTickerProviderStateMixin {
24+ late AnimationController controller;
25+ double bottomInset = 0.0 ;
26+ double yOffset = 0.0 ;
27+ double oldYOffset = 0.0 ;
28+ FocusNode ? focusNode;
29+ double focusNodeY = 0.0 ;
30+
31+ void updateFocusNode (FocusNode ? node, double screenHeight) {
32+ if (node == focusNode) {
33+ return ;
34+ }
35+ focusNode = node;
36+ if (focusNode != null ) {
37+ BuildContext ? context = focusNode! .context;
38+ if (context != null ) {
39+ RenderBox box = context.findRenderObject () as RenderBox ;
40+ Offset position = box.localToGlobal (Offset (box.size.width * 0.5 , box.size.height * 0.5 ));
41+ focusNodeY = position.dy + yOffset;
42+ if (bottomInset != 0.0 ) {
43+ double center = (screenHeight - bottomInset) * 0.5 ;
44+ double newOffset = min (max (focusNodeY - center, 0.0 ), bottomInset);
45+ animateOffset (yOffset, newOffset);
46+ }
47+ }
48+ } else {
49+ focusNodeY = 0.0 ;
50+ }
51+ }
52+
53+ void animateOffset (double from, double to) {
54+ controller.addListener (() {
55+ double t = Curves .easeOutSine.transform (controller.value);
56+ setState (() {
57+ yOffset = from + (to - from) * t;
58+ oldYOffset = yOffset;
59+ });
60+ });
61+
62+ controller.forward ();
63+ }
64+
65+ void updateBottomInset (double bottom, double screenHeight) {
66+ if (bottom != bottomInset) {
67+ if (focusNode == null ) {
68+ if (yOffset != 0.0 ) {
69+ yOffset = oldYOffset * (bottom / bottomInset);
70+ }
71+ if (bottom == 0.0 ) {
72+ yOffset = 0.0 ;
73+ oldYOffset = 0.0 ;
74+ bottomInset = bottom;
75+ }
76+ } else {
77+ bottomInset = bottom;
78+ double center = (screenHeight - bottomInset) * 0.5 ;
79+ yOffset = min (max (focusNodeY - center, 0.0 ), bottomInset);
80+ oldYOffset = yOffset;
81+ }
82+ }
83+ }
84+
85+ void handleFocusChange () {
86+ if (context.mounted) {
87+ double screenHeight = MediaQuery .of (context).size.height;
88+ updateFocusNode (
89+ FocusScope .of (context, createDependency: false ).focusedChild,
90+ screenHeight,
91+ );
92+ }
93+ }
94+
95+ @override
96+ void initState () {
97+ super .initState ();
98+ controller = AnimationController (
99+ duration: const Duration (milliseconds: 250 ),
100+ vsync: this ,
101+ );
102+
103+ controller.addStatusListener ((status) {
104+ if (status == AnimationStatus .completed) {
105+ controller.clearListeners (); // we could also save all listeners and remove them one by one
106+ controller.reset ();
107+ }
108+ });
109+
110+ FocusManager .instance.addListener (handleFocusChange);
111+ }
112+
113+ @override
114+ void dispose () {
115+ FocusManager .instance.removeListener (handleFocusChange);
116+ controller.dispose ();
117+ super .dispose ();
13118 }
14119
15120 @override
16121 Widget buildFigmaNode (BuildContext context) {
17- Matrix4 bottomInset = Matrix4 .identity ();
18- bottomInset.translate (0.0 , - MediaQuery .of (context).viewInsets.bottom * 0.7 , 0.0 );
122+ double screenHeight = MediaQuery .of (context).size.height;
123+ updateBottomInset (MediaQuery .of (context).viewInsets.bottom, screenHeight);
124+
125+ Matrix4 offset = Matrix4 .identity ()..translate (0.0 , - yOffset, 0.0 );
19126
20127 return Transform (
21- transform: bottomInset ,
128+ transform: offset ,
22129 child: SafeArea (
23130 maintainBottomViewPadding: true ,
24- child: super . buildFigmaNode (context),
131+ child: FigmaFrame . buildFigmaFrame (context, widget.model ),
25132 ),
26133 );
27134 }
28- }
135+ }
0 commit comments