Skip to content

Commit

Permalink
Merge branch 'master' into fix-1039
Browse files Browse the repository at this point in the history
  • Loading branch information
bdlukaa authored Apr 8, 2024
2 parents ab4dfbc + 86ae86b commit 99e6b44
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 26 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## next

* fix: A child of `Button` has an unbound height constraint. ([#1039](https://github.com/bdlukaa/fluent_ui/issues/1039))
* feat: Added `DatePicker.fieldFlex` to control the width proportion of each field. ([#1053](https://github.com/bdlukaa/fluent_ui/pull/1053))
* fix: `Slider` thumb is correct rendered when it's on the edges. ([#1046](https://github.com/bdlukaa/fluent_ui/pull/1046)
* feat: Added `TabView.addIconBuilder` ([#1047](https://github.com/bdlukaa/fluent_ui/pull/1047))

## 4.8.6

Expand Down
19 changes: 19 additions & 0 deletions example/lib/screens/forms/date_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class DatePickerPage extends StatefulWidget {
class _DatePickerPageState extends State<DatePickerPage> with PageMixin {
DateTime? simpleTime;
DateTime? hiddenTime;
DateTime? flexTime;

bool showYear = true;
bool showMonth = true;
Expand Down Expand Up @@ -105,6 +106,24 @@ DatePicker(
),
),
),
subtitle(content: const Text('A DatePicker with flex layout')),
CardHighlight(
codeSnippet: '''DateTime? selected;
DatePicker(
selected: selected,
fieldFlex: const [2, 3, 2], // Same order as fieldOrder
onChanged: (time) => setState(() => selected = time),
),''',
child: Align(
alignment: AlignmentDirectional.centerStart,
child: DatePicker(
selected: flexTime,
fieldFlex: const [2, 3, 2],
onChanged: (v) => setState(() => flexTime = v),
),
),
),
],
);
}
Expand Down
41 changes: 38 additions & 3 deletions lib/src/controls/form/pickers/date_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,13 @@ class DatePicker extends StatefulWidget {
this.autofocus = false,
this.locale,
this.fieldOrder,
this.fieldFlex,
}) : startDate = startDate ?? DateTime.now().subtract(kYearDuration * 100),
endDate = endDate ?? DateTime.now().add(kYearDuration * 25);
endDate = endDate ?? DateTime.now().add(kYearDuration * 25),
assert(
fieldFlex == null || fieldFlex.length == 3,
'fieldFlex must be null or have a length of 3',
);

/// The current date selected date.
///
Expand Down Expand Up @@ -153,6 +158,16 @@ class DatePicker extends StatefulWidget {
/// on the current locale
final List<DatePickerField>? fieldOrder;

/// The flex of the fields.
///
/// if null, the flex is base on the current locale.
///
/// See also:
///
/// * [getDateFlexFromLocale], which returns the flex of the fields based
/// on the current locale
final List<int>? fieldFlex;

@override
State<DatePicker> createState() => _DatePickerState();

Expand Down Expand Up @@ -268,6 +283,7 @@ class _DatePickerState extends State<DatePicker> {
final locale = widget.locale ?? Localizations.maybeLocaleOf(context);

final fieldOrder = widget.fieldOrder ?? getDateOrderFromLocale(locale);
final fieldFlex = widget.fieldFlex ?? getDateFlexFromLocale(locale);
assert(fieldOrder.isNotEmpty);
assert(
fieldOrder.where((f) => f == DatePickerField.month).length <= 1,
Expand Down Expand Up @@ -298,6 +314,7 @@ class _DatePickerState extends State<DatePicker> {
yearController: _yearController,
locale: widget.locale,
fieldOrder: fieldOrder,
fieldFlex: fieldFlex,
);
},
pickerHeight: widget.popupHeight,
Expand Down Expand Up @@ -326,7 +343,7 @@ class _DatePickerState extends State<DatePicker> {

final monthWidgets = [
Expanded(
flex: 2,
flex: fieldFlex[fieldOrder.indexOf(DatePickerField.month)],
child: Padding(
padding: widget.contentPadding,
child: Text(
Expand All @@ -343,6 +360,7 @@ class _DatePickerState extends State<DatePicker> {

final dayWidget = [
Expanded(
flex: fieldFlex[fieldOrder.indexOf(DatePickerField.day)],
child: Text(
widget.selected == null
? localizations.day
Expand All @@ -358,6 +376,7 @@ class _DatePickerState extends State<DatePicker> {

final yearWidgets = [
Expanded(
flex: fieldFlex[fieldOrder.indexOf(DatePickerField.year)],
child: Text(
widget.selected == null
? localizations.year
Expand Down Expand Up @@ -434,6 +453,7 @@ class _DatePickerContentPopUp extends StatefulWidget {
required this.endDate,
required this.locale,
required this.fieldOrder,
required this.fieldFlex,
});

final bool showMonth;
Expand All @@ -449,6 +469,7 @@ class _DatePickerContentPopUp extends StatefulWidget {
final DateTime endDate;
final Locale? locale;
final List<DatePickerField> fieldOrder;
final List<int> fieldFlex;

@override
State<_DatePickerContentPopUp> createState() =>
Expand Down Expand Up @@ -510,7 +531,8 @@ class __DatePickerContentPopUpState extends State<_DatePickerContentPopUp> {
final months = monthsInCurrentYear;
final monthWidget = [
Expanded(
flex: 2,
flex:
widget.fieldFlex[widget.fieldOrder.indexOf(DatePickerField.month)],
child: () {
final formatter = DateFormat.MMMM(locale.toString());
// MONTH
Expand Down Expand Up @@ -585,6 +607,7 @@ class __DatePickerContentPopUpState extends State<_DatePickerContentPopUp> {

final dayWidget = [
Expanded(
flex: widget.fieldFlex[widget.fieldOrder.indexOf(DatePickerField.day)],
child: () {
// DAY
final daysInMonth = _getDaysInMonth(localDate.month, localDate.year);
Expand Down Expand Up @@ -661,6 +684,7 @@ class __DatePickerContentPopUpState extends State<_DatePickerContentPopUp> {

final yearWidget = [
Expanded(
flex: widget.fieldFlex[widget.fieldOrder.indexOf(DatePickerField.year)],
child: () {
final years = widget.endDate.year - widget.startDate.year + 1;
final formatter = DateFormat.y(locale.toString());
Expand Down Expand Up @@ -810,3 +834,14 @@ List<DatePickerField> getDateOrderFromLocale(Locale? locale) {

return dmy;
}

/// Get the date flex based on the current locale.
/// The flex is used to determine the width of the fields.
List<int> getDateFlexFromLocale(Locale? locale) {
final lang = locale?.languageCode;
if (locale?.countryCode?.toLowerCase() == 'us') return const [2, 1, 1];

if (['zh', 'ko'].contains(lang)) return const [1, 1, 1];

return [1, 2, 1];
}
6 changes: 4 additions & 2 deletions lib/src/controls/inputs/slider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,8 @@ class _SliderState extends State<Slider> {

/// This is used to remove the padding the Material Slider adds automatically
class _CustomTrackShape extends m.RoundedRectSliderTrackShape {
static const double _trackSidePadding = 10.0;

@override
Rect getPreferredRect({
required RenderBox parentBox,
Expand All @@ -391,9 +393,9 @@ class _CustomTrackShape extends m.RoundedRectSliderTrackShape {
bool isDiscrete = false,
}) {
final trackHeight = sliderTheme.trackHeight!;
final trackLeft = offset.dx;
final trackLeft = offset.dx + _trackSidePadding;
final trackTop = offset.dy + (parentBox.size.height - trackHeight) / 2;
final trackWidth = parentBox.size.width;
final trackWidth = parentBox.size.width - (2 * _trackSidePadding);
return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
}

Expand Down
13 changes: 12 additions & 1 deletion lib/src/controls/navigation/tab_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class TabView extends StatefulWidget {
required this.tabs,
this.onNewPressed,
this.addIconData = FluentIcons.add,
this.addIconBuilder,
this.shortcutsEnabled = true,
this.onReorder,
this.showScrollButtons = true,
Expand Down Expand Up @@ -91,6 +92,13 @@ class TabView extends StatefulWidget {

/// The icon of the new button
final IconData addIconData;

/// The builder for the add icon.
///
/// This does not build the add button, only its icon.
///
/// When null, the add icon is rendered.
final Widget Function(Widget addIcon)? addIconBuilder;

/// Whether the following shortcuts are enabled:
///
Expand Down Expand Up @@ -562,7 +570,10 @@ class _TabViewState extends State<TabView> {
),
child: _buttonTabBuilder(
context,
Icon(widget.addIconData, size: 12.0),
widget.addIconBuilder?.call(
Icon(widget.addIconData, size: 12.0),
) ??
Icon(widget.addIconData, size: 12.0),
widget.onNewPressed!,
localizations.newTabLabel,
),
Expand Down
45 changes: 25 additions & 20 deletions lib/src/controls/navigation/tree_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -227,25 +227,30 @@ class TreeViewItem with Diagnosticable {
/// its child items. Useful if you want to have multiple trees with the
/// same items, but with different UX states (e.g., selection, visibility,
/// etc.).
TreeViewItem.from(TreeViewItem source)
: this(
key: source.key,
leading: source.leading,
content: source.content,
value: source.value,
children: source.children.map(TreeViewItem.from).toList(),
collapsable: source.collapsable,
expanded: source.expanded,
selected: source.selected,
onInvoked: source.onInvoked,
onExpandToggle: source.onExpandToggle,
backgroundColor: source.backgroundColor,
autofocus: source.autofocus,
focusNode: source.focusNode,
semanticLabel: source.semanticLabel,
loadingWidget: source.loadingWidget,
lazy: source.lazy,
);
factory TreeViewItem.from(TreeViewItem source) {
final newItem = TreeViewItem(
key: source.key,
leading: source.leading,
content: source.content,
value: source.value,
children: source.children.map(TreeViewItem.from).toList(),
collapsable: source.collapsable,
expanded: source.expanded,
selected: source.selected,
onInvoked: source.onInvoked,
onExpandToggle: source.onExpandToggle,
backgroundColor: source.backgroundColor,
autofocus: source.autofocus,
focusNode: source.focusNode,
semanticLabel: source.semanticLabel,
loadingWidget: source.loadingWidget,
lazy: source.lazy,
);
for (final c in newItem.children) {
c._parent = newItem;
}
return newItem;
}

/// Whether this node is expandable
bool get isExpandable {
Expand Down Expand Up @@ -428,7 +433,7 @@ extension TreeViewItemCollection on List<TreeViewItem> {
if (isNotEmpty) {
final list = <TreeViewItem>[];
final anyExpandableSiblings = any((i) => i.isExpandable);
for (final item in [...this]) {
for (final item in this) {
item
.._parent = parent
.._anyExpandableSiblings = anyExpandableSiblings;
Expand Down
47 changes: 47 additions & 0 deletions test/tree_view_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,53 @@ void main() {
await tester.pumpWidget(wrapApp(child: TreeView(items: items)));

expect(itemOne.parent, isNotNull);
expect(itemOne.parent, items[0]);
expect(itemOne.parent?.selected, null);
});

testWidgets('TreeViewItem deep copy rebuilds parent linkage',
(WidgetTester tester) async {
final items = [
TreeViewItem(
content: const Text('Parent item'),
children: [
TreeViewItem(
content: const Text('Item 1'),
children: [
TreeViewItem(
content: const Text('Subitem 1'),
),
TreeViewItem(
content: const Text('Subitem 2'),
),
],
),
TreeViewItem(
content: const Text('Item 2'),
),
TreeViewItem(
content: const Text('Item 3'),
),
],
),
];

await tester.pumpWidget(wrapApp(child: TreeView(items: items)));
expect(items[0].parent, isNull);
expect(items[0].children[0].parent, items[0]);
expect(items[0].children[1].parent, items[0]);
expect(items[0].children[2].parent, items[0]);
expect(items[0].children[0].children[0].parent, items[0].children[0]);
expect(items[0].children[0].children[1].parent, items[0].children[0]);

final itemsCopy = items.map(TreeViewItem.from).toList();
expect(itemsCopy[0].parent, isNull);
expect(itemsCopy[0].children[0].parent, itemsCopy[0]);
expect(itemsCopy[0].children[1].parent, itemsCopy[0]);
expect(itemsCopy[0].children[2].parent, itemsCopy[0]);
expect(
itemsCopy[0].children[0].children[0].parent, itemsCopy[0].children[0]);
expect(
itemsCopy[0].children[0].children[1].parent, itemsCopy[0].children[0]);
});
}

0 comments on commit 99e6b44

Please sign in to comment.