Skip to content

Commit ac53dde

Browse files
committed
refactor(perf): Reduce calls to setState and frames scheduling in several widgets.
1 parent 9159c81 commit ac53dde

File tree

8 files changed

+30
-37
lines changed

8 files changed

+30
-37
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
- **MINOR BREAKING** refactor: Remove `Brightness.isLight`, `Brightness.isDark` and `Brightness.opposite` extension methods. Use `switch` statements instead.
2121
- feat: Add latest color resources from Microsoft UI XAML.
2222
- refactor(perf): Optimize animation handling in Scrollbar, NavigationView, Acrylic and buttons.
23+
- refactor(perf): Reduce calls to `setState` and frames scheduling in several widgets.
2324

2425
## 4.13.0
2526

lib/src/controls/form/auto_suggest_box.dart

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -568,13 +568,10 @@ class AutoSuggestBoxState<T> extends State<AutoSuggestBox<T>> {
568568
} else if (_controller.text.isNotEmpty) {
569569
showOverlay();
570570
}
571-
setState(() {});
572571
}
573572

574573
void _handleTextChanged() {
575574
if (!mounted) return;
576-
if (_controller.text.length < 2) setState(() {});
577-
578575
_updateLocalItems();
579576

580577
// Update the overlay when the text box size has changed
@@ -674,7 +671,6 @@ class AutoSuggestBoxState<T> extends State<AutoSuggestBox<T>> {
674671

675672
if (_textBoxKey.currentContext != null) {
676673
overlayState.insert(_entry!);
677-
if (mounted) setState(() {});
678674
}
679675
}
680676

@@ -945,7 +941,7 @@ class _AutoSuggestBoxOverlayState<T> extends State<_AutoSuggestBoxOverlay<T>> {
945941
duration: theme.fastAnimationDuration,
946942
curve: Curves.easeInOut,
947943
);
948-
setState(() {});
944+
if (mounted) setState(() {});
949945
});
950946
itemsSubscription = widget.itemsStream.listen((items) {
951947
this.items = items;

lib/src/controls/inputs/split_button.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ class SplitButtonState extends State<SplitButton> {
151151

152152
/// Shows the flyout attached to the dropdown button
153153
Future<void> showFlyout() async {
154-
setState(() {});
155154
await flyoutController.showFlyout<void>(
156155
barrierColor: Colors.transparent,
157156
autoModeConfiguration: FlyoutAutoConfiguration(
@@ -161,7 +160,6 @@ class SplitButtonState extends State<SplitButton> {
161160
return widget.flyout;
162161
},
163162
);
164-
if (mounted) setState(() {});
165163
}
166164

167165
void _updateFocusHighlight(bool focused) {

lib/src/controls/navigation/navigation_view/pane.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -932,7 +932,7 @@ class _MenuFlyoutPaneItem extends MenuFlyoutItemBase {
932932
).add(padding ?? EdgeInsetsDirectional.zero),
933933
height: 36,
934934
color: ButtonThemeData.uncheckedInputColor(
935-
FluentTheme.of(context),
935+
fluentTheme,
936936
states,
937937
transparentWhenNone: true,
938938
transparentWhenDisabled: true,

lib/src/controls/navigation/navigation_view/view.dart

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -389,17 +389,13 @@ class NavigationViewState extends State<NavigationView> {
389389

390390
void _generateKeys() {
391391
if (widget.pane == null) return;
392-
_itemKeys
393-
..clear()
394-
..addAll(
395-
Map.fromIterables(
396-
List.generate(widget.pane!.effectiveItems.length, (i) => i),
397-
List.generate(
398-
widget.pane!.effectiveItems.length,
399-
(i) => GlobalKey(debugLabel: 'NavigationView item key#$i'),
400-
),
401-
),
402-
);
392+
final itemCount = widget.pane!.effectiveItems.length;
393+
if (_itemKeys.length == itemCount) return;
394+
395+
_itemKeys.clear();
396+
for (var i = 0; i < itemCount; i++) {
397+
_itemKeys[i] = GlobalKey(debugLabel: 'NavigationView item key#$i');
398+
}
403399
}
404400

405401
@override
@@ -445,7 +441,9 @@ class NavigationViewState extends State<NavigationView> {
445441
);
446442

447443
final theme = NavigationPaneTheme.of(context);
444+
final fluentTheme = FluentTheme.of(context);
448445
final localizations = FluentLocalizations.of(context);
446+
final mediaQuerySize = MediaQuery.sizeOf(context);
449447
final EdgeInsetsGeometry appBarPadding = EdgeInsetsDirectional.only(
450448
top: widget.appBar?.finalHeight(context) ?? 0.0,
451449
);
@@ -486,7 +484,7 @@ class NavigationViewState extends State<NavigationView> {
486484
/// (641px to 1007px).
487485
/// - Only a menu button (minimal) on small window widths (640px or less).
488486
var width = consts.biggest.width;
489-
if (width.isInfinite) width = MediaQuery.sizeOf(context).width;
487+
if (width.isInfinite) width = mediaQuerySize.width;
490488

491489
PaneDisplayMode autoDisplayMode;
492490
if (width <= 640) {
@@ -801,7 +799,7 @@ class NavigationViewState extends State<NavigationView> {
801799
end: 0,
802800
height: widget.appBar?.finalHeight(context) ?? 0.0,
803801
child: ColoredBox(
804-
color: FluentTheme.of(context).scaffoldBackgroundColor,
802+
color: fluentTheme.scaffoldBackgroundColor,
805803
),
806804
),
807805
PositionedDirectional(
@@ -829,7 +827,7 @@ class NavigationViewState extends State<NavigationView> {
829827
curve: theme.animationCurve ?? Curves.linear,
830828
start: minimalPaneOpen ? 0.0 : -openSize,
831829
width: openSize,
832-
height: MediaQuery.sizeOf(context).height,
830+
height: mediaQuerySize.height,
833831
onEnd: () {
834832
_isTransitioning = false;
835833
if (mounted) setState(() {});
@@ -1014,6 +1012,7 @@ class NavigationAppBar with Diagnosticable {
10141012
assert(debugCheckHasFluentLocalizations(context));
10151013
assert(debugCheckHasFluentTheme(context));
10161014
final localizations = FluentLocalizations.of(context);
1015+
final localFluentTheme = FluentTheme.of(context);
10171016
final onPressed = canPop ? () => Navigator.maybePop(context) : null;
10181017
widget = NavigationPaneTheme(
10191018
data: NavigationPaneTheme.of(context).merge(
@@ -1023,7 +1022,7 @@ class NavigationAppBar with Diagnosticable {
10231022
return ButtonThemeData.buttonColor(context, states);
10241023
}
10251024
return ButtonThemeData.uncheckedInputColor(
1026-
FluentTheme.of(context),
1025+
localFluentTheme,
10271026
states,
10281027
).basedOnLuminance();
10291028
}),
@@ -1088,6 +1087,7 @@ class _NavigationAppBar extends StatelessWidget {
10881087
if (appBar.title != null) {
10891088
assert(debugCheckHasFluentTheme(context));
10901089
final theme = NavigationPaneTheme.of(context);
1090+
final fluentTheme = FluentTheme.of(context);
10911091

10921092
return AnimatedPadding(
10931093
duration: theme.animationDuration ?? Duration.zero,
@@ -1096,7 +1096,7 @@ class _NavigationAppBar extends StatelessWidget {
10961096
const EdgeInsetsDirectional.only(start: 6),
10971097
),
10981098
child: DefaultTextStyle.merge(
1099-
style: FluentTheme.of(context).typography.caption,
1099+
style: fluentTheme.typography.caption,
11001100
maxLines: 1,
11011101
softWrap: false,
11021102
child: appBar.title!,
@@ -1154,13 +1154,13 @@ class _NavigationAppBar extends StatelessWidget {
11541154
default:
11551155
return const SizedBox.shrink();
11561156
}
1157-
final topPadding = MediaQuery.paddingOf(context).top;
1157+
final mediaQueryPadding = MediaQuery.paddingOf(context);
11581158

11591159
return Container(
11601160
color: appBar.backgroundColor,
11611161
decoration: appBar.decoration,
11621162
height: appBar.finalHeight(context),
1163-
padding: EdgeInsetsDirectional.only(top: topPadding),
1163+
padding: EdgeInsetsDirectional.only(top: mediaQueryPadding.top),
11641164
child: result,
11651165
);
11661166
}

lib/src/controls/navigation/tab_view/tab_view.dart

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -283,13 +283,7 @@ class _TabViewState extends State<TabView> {
283283
itemCount: widget.tabs.length,
284284
animationDuration: const Duration(milliseconds: 100),
285285
);
286-
scrollController
287-
..itemCount = widget.tabs.length
288-
..addListener(_handleScrollUpdate);
289-
}
290-
291-
void _handleScrollUpdate() {
292-
if (mounted) setState(() {});
286+
scrollController.itemCount = widget.tabs.length;
293287
}
294288

295289
@override

lib/src/controls/pickers/time_picker.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:fluent_ui/src/controls/pickers/pickers.dart';
33
import 'package:fluent_ui/src/intl_script_locale_apply_mixin.dart';
44
import 'package:flutter/foundation.dart';
55
import 'package:flutter/rendering.dart';
6+
import 'package:flutter/scheduler.dart';
67
import 'package:intl/intl.dart';
78

89
String _formatHour(int hour, String locale) {
@@ -449,9 +450,11 @@ class __TimePickerContentPopupState extends State<_TimePickerContentPopup> {
449450

450451
void handleDateChanged(DateTime time) {
451452
localDate = time;
452-
Future<void>.delayed(const Duration(milliseconds: 1), () {
453-
if (mounted) setState(() {});
454-
});
453+
if (mounted) {
454+
SchedulerBinding.instance.addPostFrameCallback((_) {
455+
if (mounted) setState(() {});
456+
});
457+
}
455458
}
456459

457460
int getClosestMinute(List<int> possibleMinutes, int goal) {

lib/src/controls/surfaces/commandbar.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,11 @@ class CommandBarState extends State<CommandBar> {
276276
);
277277
},
278278
);
279-
// Update the widget to update the tooltip of the default overflow item
279+
// Update immediately to show tooltip change
280280
if (mounted) setState(() {});
281281

282282
await future;
283+
// Update after flyout closes to restore visual state
283284
if (mounted) setState(() {});
284285
}
285286

0 commit comments

Comments
 (0)