mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-31 06:58:42 +01:00
Upstream reformatted its whole tree in 16ddc0837. Matching it removes
formatting from the conflict surface of this and every later sync.
164 lines
5.1 KiB
Dart
164 lines
5.1 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
|
|
class LineTabSwitcher extends StatefulWidget {
|
|
const LineTabSwitcher({
|
|
super.key,
|
|
required this.tabs,
|
|
required this.onTabChange,
|
|
required this.selectedTab,
|
|
});
|
|
|
|
final List<String> tabs;
|
|
final void Function(int index) onTabChange;
|
|
final int selectedTab;
|
|
|
|
@override
|
|
State<LineTabSwitcher> createState() => _LineTabSwitcherState();
|
|
}
|
|
|
|
class _LineTabSwitcherState extends State<LineTabSwitcher> {
|
|
List<GlobalKey> textWidgetKeys = [];
|
|
List<Size> textWidgetSizes = [];
|
|
double totalWidth = 0;
|
|
final double itemPadding = 16;
|
|
final double barPadding = 8;
|
|
bool textWidgetsMeasured = false;
|
|
|
|
@override
|
|
void didUpdateWidget(covariant LineTabSwitcher oldWidget) {
|
|
super.didUpdateWidget(oldWidget);
|
|
|
|
if (!listEquals(oldWidget.tabs, widget.tabs)) {
|
|
textWidgetKeys = List.generate(widget.tabs.length, (_) => GlobalKey());
|
|
textWidgetSizes = [];
|
|
totalWidth = 0;
|
|
textWidgetsMeasured = false;
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) => measure());
|
|
}
|
|
}
|
|
|
|
double _calcBarLeft() {
|
|
double left = 0;
|
|
|
|
if (textWidgetKeys.isEmpty || textWidgetSizes.isEmpty) {
|
|
return 0;
|
|
}
|
|
|
|
for (int i = 0; i < widget.selectedTab; i++) {
|
|
left += textWidgetSizes[i].width + itemPadding;
|
|
}
|
|
|
|
left += barPadding;
|
|
|
|
return left;
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
textWidgetKeys = List.generate(widget.tabs.length, (index) => GlobalKey());
|
|
WidgetsBinding.instance.addPostFrameCallback((_) => measure());
|
|
}
|
|
|
|
void measure() {
|
|
if (textWidgetsMeasured) return;
|
|
setState(() {
|
|
textWidgetSizes =
|
|
textWidgetKeys.map((k) => k.currentContext!.size).whereType<Size>().toList();
|
|
|
|
for (final size in textWidgetSizes) {
|
|
totalWidth += size.width + itemPadding;
|
|
}
|
|
totalWidth += barPadding;
|
|
textWidgetsMeasured = true;
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (!textWidgetsMeasured) {
|
|
WidgetsBinding.instance.addPostFrameCallback((_) => measure());
|
|
}
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
Container(
|
|
height: 28,
|
|
child: Row(
|
|
children: widget.tabs.map((item) {
|
|
final index = widget.tabs.indexOf(item);
|
|
|
|
// One tab = one node: the visible label merges into a button node
|
|
// that also carries the selected state of this tab.
|
|
return MergeSemantics(
|
|
child: Semantics(
|
|
button: true,
|
|
selected: widget.selectedTab == index,
|
|
inMutuallyExclusiveGroup: true,
|
|
child: GestureDetector(
|
|
key: ValueKey("line_tab_switcher_${index}_key"),
|
|
onTap: () {
|
|
widget.onTabChange(index);
|
|
},
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
AnimatedDefaultTextStyle(
|
|
duration: Duration(milliseconds: 150),
|
|
style: DefaultTextStyle.of(context).style.copyWith(
|
|
inherit: true,
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w500,
|
|
color: widget.selectedTab == index
|
|
? Theme.of(context).colorScheme.onSurface
|
|
: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
),
|
|
child: Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: itemPadding / 2),
|
|
child: Text(
|
|
item,
|
|
key: textWidgetKeys[index],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}).toList())),
|
|
if (widget.tabs.length > 1)
|
|
Container(
|
|
width: totalWidth,
|
|
height: 2,
|
|
child: Stack(
|
|
children: [
|
|
AnimatedPositioned(
|
|
curve: Curves.easeOut,
|
|
left: _calcBarLeft(),
|
|
bottom: 0,
|
|
duration: Duration(milliseconds: 150),
|
|
child: AnimatedSize(
|
|
duration: Duration(milliseconds: 150),
|
|
child: Container(
|
|
height: 2,
|
|
width:
|
|
textWidgetSizes.isEmpty ? 0 : textWidgetSizes[widget.selectedTab].width,
|
|
color: Theme.of(context).colorScheme.onSurface,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|