Skip to content

Commit

Permalink
Ready for checking, controller implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
Daviiddoo committed Sep 11, 2024
1 parent 4ea70a3 commit f8aef79
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 46 deletions.
7 changes: 2 additions & 5 deletions forui/example/lib/sandbox.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';

import 'package:forui/forui.dart';
import 'package:forui/src/widgets/accordion.dart';

class Sandbox extends StatefulWidget {
const Sandbox({super.key});
Expand All @@ -24,15 +23,13 @@ class _SandboxState extends State<Sandbox> {
children: [
FAccordion(
title: 'Is it Accessible?',
childHeight: 100,
initiallyExpanded: false,
onExpanded: () {},
controller: FAccordionController(initiallyExpanded: false),
child: const Text(
'Yes. It adheres to the WAI-ARIA design pattern',
textAlign: TextAlign.left,
),
),
SizedBox(height: 20),
const SizedBox(height: 20),
FSelectGroup(
label: const Text('Select Group'),
description: const Text('Select Group Description'),
Expand Down
1 change: 1 addition & 0 deletions forui/lib/forui.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export 'assets.dart';
export 'foundation.dart';
export 'theme.dart';

export 'widgets/accordion.dart';
export 'widgets/alert.dart';
export 'widgets/avatar.dart';
export 'widgets/badge.dart';
Expand Down
4 changes: 1 addition & 3 deletions forui/lib/src/theme/theme_data.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:forui/src/widgets/accordion.dart';

import 'package:meta/meta.dart';

Expand Down Expand Up @@ -138,7 +137,7 @@ final class FThemeData with Diagnosticable {
colorScheme: colorScheme,
typography: typography,
style: style,
accordionStyle: FAccordionStyle.inherit(colorScheme: colorScheme, style: style, typography: typography),
accordionStyle: FAccordionStyle.inherit(colorScheme: colorScheme, typography: typography),
alertStyles: FAlertStyles.inherit(colorScheme: colorScheme, typography: typography, style: style),
avatarStyle: FAvatarStyle.inherit(colorScheme: colorScheme, typography: typography),
badgeStyles: FBadgeStyles.inherit(colorScheme: colorScheme, typography: typography, style: style),
Expand Down Expand Up @@ -290,7 +289,6 @@ final class FThemeData with Diagnosticable {
..add(DiagnosticsProperty('scaffoldStyle', scaffoldStyle, level: DiagnosticLevel.debug))
..add(DiagnosticsProperty('selectGroupStyle', selectGroupStyle, level: DiagnosticLevel.debug))
..add(DiagnosticsProperty('switchStyle', switchStyle, level: DiagnosticLevel.debug));

}

@override
Expand Down
134 changes: 96 additions & 38 deletions forui/lib/src/widgets/accordion.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,86 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'dart:math' as math;

import 'package:forui/forui.dart';
import 'package:forui/src/foundation/tappable.dart';
import 'package:forui/src/foundation/util.dart';
import 'package:meta/meta.dart';

/// A controller that stores the expanded state of an [FAccordion].
class FAccordionController extends ValueNotifier<bool> {
bool _expanded;

/// Creates a [FAccordionController].
FAccordionController({
bool? initiallyExpanded,
}) : _expanded = initiallyExpanded ?? true,
super(initiallyExpanded ?? true);

/// whether the accordion is expanded.
bool get expanded => _expanded;

/// Toggles the expansion state of the accordion.
bool toggle() {
_expanded = !_expanded;
notifyListeners();
return _expanded;
}
}

/// A vertically stacked set of interactive headings that each reveal a section of content.
///
/// See:
/// * https://forui.dev/docs/accordion for working examples.
class FAccordion extends StatefulWidget {
/// The divider's style. Defaults to the appropriate style in [FThemeData.dividerStyles].
/// The accordion's style. Defaults to the appropriate style in [FThemeData.accordionStyle].
final FAccordionStyle? style;

/// The title.
final String title;
final bool initiallyExpanded;
final VoidCallback onExpanded;
final double childHeight;
final double removeChildAnimationPercentage;

/// The accordion's controller.
final FAccordionController controller;

/// The child.
final Widget child;

/// Creates an [FAccordion].
const FAccordion({
required this.child,
required this.childHeight,
required this.initiallyExpanded,
required this.onExpanded,
required this.controller,
this.title = '',
this.style,
this.removeChildAnimationPercentage = 0,
super.key,
});

@override
//ignore:library_private_types_in_public_api
_FAccordionState createState() => _FAccordionState();

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('style', style))
..add(StringProperty('title', title))
..add(DiagnosticsProperty('controller', controller));
}
}

class _FAccordionState extends State<FAccordion> with SingleTickerProviderStateMixin {
late Animation<double> animation;
late AnimationController controller;
bool _isExpanded = false;
bool _hovered = false;

@override
void initState() {
super.initState();
_isExpanded = widget.initiallyExpanded;
controller = AnimationController(
duration: const Duration(milliseconds: 500),
value: _isExpanded ? 1.0 : 0.0,
value: widget.controller.expanded ? 1.0 : 0.0,
vsync: this,
);
animation = Tween<double>(
Expand All @@ -60,8 +95,6 @@ class _FAccordionState extends State<FAccordion> with SingleTickerProviderStateM
)..addListener(() {
setState(() {});
});

_isExpanded ? controller.forward() : controller.reverse();
}

@override
Expand All @@ -73,15 +106,7 @@ class _FAccordionState extends State<FAccordion> with SingleTickerProviderStateM
onEnter: (_) => setState(() => _hovered = true),
onExit: (_) => setState(() => _hovered = false),
child: FTappable(
onPress: () {
if (_isExpanded) {
controller.reverse();
} else {
controller.forward();
}
setState(() => _isExpanded = !_isExpanded);
widget.onExpanded();
},
onPress: () => widget.controller.toggle() ? controller.forward() : controller.reverse(),
child: Container(
padding: style.titlePadding,
child: Row(
Expand All @@ -102,10 +127,7 @@ class _FAccordionState extends State<FAccordion> with SingleTickerProviderStateM
),
Transform.rotate(
angle: (animation.value / 100 * -180 + 90) * math.pi / 180.0,
child: FAssets.icons.chevronRight(
height: 20,
colorFilter: const ColorFilter.mode(Colors.black, BlendMode.srcIn),
),
child: style.icon,
),
],
),
Expand All @@ -126,8 +148,7 @@ class _FAccordionState extends State<FAccordion> with SingleTickerProviderStateM
),
),
FDivider(
style: context.theme.dividerStyles.horizontal
.copyWith(padding: EdgeInsets.zero, color: context.theme.colorScheme.border),
style: context.theme.dividerStyles.horizontal.copyWith(padding: EdgeInsets.zero, color: style.dividerColor),
),
],
);
Expand All @@ -138,6 +159,14 @@ class _FAccordionState extends State<FAccordion> with SingleTickerProviderStateM
controller.dispose();
super.dispose();
}

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty('animation', animation))
..add(DiagnosticsProperty('controller', controller));
}
}

/// The [FAccordion] styles.
Expand All @@ -151,28 +180,49 @@ final class FAccordionStyle with Diagnosticable {
/// The padding of the content.
final EdgeInsets contentPadding;

/// The icon.
final SvgPicture icon;

/// The divider's color.
final Color dividerColor;

/// Creates a [FAccordionStyle].
FAccordionStyle({required this.title, required this.titlePadding, required this.contentPadding});
FAccordionStyle({
required this.title,
required this.titlePadding,
required this.contentPadding,
required this.icon,
required this.dividerColor,
});

/// Creates a [FDividerStyles] that inherits its properties from [colorScheme] and [style].
FAccordionStyle.inherit({required FColorScheme colorScheme, required FStyle style, required FTypography typography})
/// Creates a [FDividerStyles] that inherits its properties from [colorScheme].
FAccordionStyle.inherit({required FColorScheme colorScheme, required FTypography typography})
: title = typography.base.copyWith(
fontWeight: FontWeight.w500,
),
titlePadding = const EdgeInsets.symmetric(vertical: 15),
contentPadding = const EdgeInsets.only(bottom: 15);
contentPadding = const EdgeInsets.only(bottom: 15),
icon = FAssets.icons.chevronRight(
height: 20,
colorFilter: ColorFilter.mode(colorScheme.primary, BlendMode.srcIn),
),
dividerColor = colorScheme.border;

/// Returns a copy of this [FAccordionStyle] with the given properties replaced.
@useResult
FAccordionStyle copyWith({
TextStyle? title,
EdgeInsets? titlePadding,
EdgeInsets? contentPadding,
SvgPicture? icon,
Color? dividerColor,
}) =>
FAccordionStyle(
title: title ?? this.title,
titlePadding: titlePadding ?? this.titlePadding,
contentPadding: contentPadding ?? this.contentPadding,
icon: icon ?? this.icon,
dividerColor: dividerColor ?? this.dividerColor,
);

@override
Expand All @@ -181,7 +231,8 @@ final class FAccordionStyle with Diagnosticable {
properties
..add(DiagnosticsProperty('title', title))
..add(DiagnosticsProperty('padding', titlePadding))
..add(DiagnosticsProperty('contentPadding', contentPadding));
..add(DiagnosticsProperty('contentPadding', contentPadding))
..add(ColorProperty('dividerColor', dividerColor));
}

@override
Expand All @@ -191,13 +242,15 @@ final class FAccordionStyle with Diagnosticable {
runtimeType == other.runtimeType &&
title == other.title &&
titlePadding == other.titlePadding &&
contentPadding == other.contentPadding;
contentPadding == other.contentPadding &&
icon == other.icon &&
dividerColor == other.dividerColor;

@override
int get hashCode => title.hashCode ^ titlePadding.hashCode ^ contentPadding.hashCode;
int get hashCode =>
title.hashCode ^ titlePadding.hashCode ^ contentPadding.hashCode ^ icon.hashCode ^ dividerColor.hashCode;
}


class _Expandable extends SingleChildRenderObjectWidget {
final double _percentage;

Expand Down Expand Up @@ -228,7 +281,6 @@ class _ExpandableBox extends RenderBox with RenderObjectWithChildMixin<RenderBox
if (child case final child?) {
child.layout(constraints.normalize(), parentUsesSize: true);
size = Size(child.size.width, child.size.height * _percentage);

} else {
size = constraints.smallest;
}
Expand Down Expand Up @@ -261,6 +313,12 @@ class _ExpandableBox extends RenderBox with RenderObjectWithChildMixin<RenderBox
_percentage = value;
markNeedsLayout();
}

@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DoubleProperty('percentage', percentage));
}
}

class _Clipper extends CustomClipper<Rect> {
Expand Down
8 changes: 8 additions & 0 deletions forui/lib/widgets/accordion.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/// {@category Widgets}
///
/// An image element with a fallback for representing the user.
///
/// See https://forui.dev/docs/avatar for working examples.
library forui.widgets.accordion;

export '../src/widgets/accordion.dart';

0 comments on commit f8aef79

Please sign in to comment.