Vaani/lib/shared/widgets/expandable_description.dart

125 lines
4.1 KiB
Dart
Raw Normal View History

2024-05-12 05:38:30 -04:00
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class ExpandableDescription extends HookWidget {
const ExpandableDescription({
super.key,
required this.title,
required this.content,
this.readMoreText = 'Read More',
this.readLessText = 'Read Less',
});
/// the title of the description section
final String title;
/// the collapsible content
final String content;
/// the text to show when the description is collapsed
final String readMoreText;
/// the text to show when the description is expanded
final String readLessText;
@override
Widget build(BuildContext context) {
var isDescExpanded = useState(false);
const duration = Duration(milliseconds: 300);
final descriptionAnimationController = useAnimationController(
duration: duration,
);
final themeData = Theme.of(context);
final textTheme = themeData.textTheme;
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// header with carrot icon is tapable
InkWell(
borderRadius: BorderRadius.circular(8),
onTap: () {
isDescExpanded.value = !isDescExpanded.value;
if (isDescExpanded.value) {
descriptionAnimationController.forward();
} else {
descriptionAnimationController.reverse();
}
},
// a header with a carrot icon
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// header text
Text(
style: textTheme.titleMedium,
title,
),
// carrot icon
AnimatedRotation(
turns: isDescExpanded.value ? 0.5 : 0,
duration: duration,
curve: Curves.easeInOutCubic,
child: const Icon(Icons.expand_more_rounded),
),
],
),
),
),
// description with maxLines of 3
// for now leave animation, just toggle the maxLines
// TODO: add animation using custom ticker that will animate the maxLines
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: AnimatedSwitcher(
duration: duration * 3,
child: isDescExpanded.value
? Text(
style: textTheme.bodyMedium,
content,
maxLines: null,
)
: Text(
style: textTheme.bodyMedium,
content,
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
),
),
// a Read More / Read Less button at the end of the description
// if the description is expanded, then the button will say Read Less
// if the description is collapsed, then the button will say Read More
// the button will be at the end of the description
// the button will be tapable
Align(
alignment: Alignment.centerRight,
child: TextButton(
onPressed: () {
isDescExpanded.value = !isDescExpanded.value;
if (isDescExpanded.value) {
descriptionAnimationController.forward();
} else {
descriptionAnimationController.reverse();
}
},
child: Text(
style: textTheme.bodySmall?.copyWith(
color: themeData.colorScheme.primary,
),
isDescExpanded.value ? readLessText : readMoreText,
),
),
),
],
),
);
}
}