Skip to content

Commit

Permalink
style: migrated to expandable page view for better layout
Browse files Browse the repository at this point in the history
  • Loading branch information
RyanYuuki committed Nov 25, 2024
1 parent 6a597c9 commit 4ae7fc0
Show file tree
Hide file tree
Showing 14 changed files with 2,177 additions and 0 deletions.
63 changes: 63 additions & 0 deletions lib/components/android/common/IconWithLabel.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import 'package:flutter/material.dart';

class iconWithName extends StatelessWidget {
final IconData icon;
final String name;
final bool isVertical;
final Color color;
final double size;
final Color backgroundColor;
final BorderRadius? borderRadius;
final Color TextColor;
final double fontSize;
final bool isGapped;
const iconWithName({
super.key,
required this.icon,
required this.name,
this.isVertical = true,
this.color = Colors.black,
this.size = 16.0,
this.fontSize = 12.0,
this.backgroundColor = Colors.white,
this.borderRadius,
this.TextColor = Colors.black,
this.isGapped = true,
});

@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 6),
height: 20,
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: borderRadius,
),
child: isVertical
? Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, size: size, color: color),
const SizedBox(height: 8.0),
Text(name, style: const TextStyle(fontSize: 14.0)),
],
)
: Center(
child: Row(
children: [
Icon(icon, size: size, color: color),
SizedBox(width: isGapped ? 2.0 : 0.0),
Text(name,
style: TextStyle(
fontSize: fontSize,
color: TextColor,
fontWeight: FontWeight.bold)),
],
),
),
);
}
}
138 changes: 138 additions & 0 deletions lib/components/android/common/expandable_page_view.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import 'package:flutter/material.dart';

class ExpandablePageView extends StatefulWidget {
final int itemCount;
final Widget Function(BuildContext, int) itemBuilder;
final PageController? controller;
final ValueChanged<int>? onPageChanged;
final bool reverse;
final double defaultHeight;

const ExpandablePageView({
required this.itemCount,
required this.itemBuilder,
this.controller,
this.onPageChanged,
this.reverse = false,
this.defaultHeight = 1750,
super.key,
});

@override
_ExpandablePageViewState createState() => _ExpandablePageViewState();
}

class _ExpandablePageViewState extends State<ExpandablePageView> {
PageController? _pageController;
late List<double> _heights;
int _currentPage = 0;

double get _currentHeight => _heights[_currentPage];

@override
void initState() {
super.initState();
_heights =
List.filled(widget.itemCount, widget.defaultHeight, growable: true);
_pageController = widget.controller ?? PageController();
_pageController?.addListener(_updatePage);
}

@override
void dispose() {
_pageController?.removeListener(_updatePage);
_pageController?.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return TweenAnimationBuilder<double>(
curve: Curves.easeInOutCubic,
tween: Tween<double>(begin: _heights.first, end: _currentHeight),
duration: const Duration(milliseconds: 300),
builder: (context, value, child) => SizedBox(height: value, child: child),
child: PageView.builder(
controller: _pageController,
itemCount: widget.itemCount,
physics: const BouncingScrollPhysics(),
itemBuilder: _itemBuilder,
onPageChanged: widget.onPageChanged,
reverse: widget.reverse,
),
);
}

Widget _itemBuilder(BuildContext context, int index) {
final item = widget.itemBuilder(context, index);
return OverflowBox(
minHeight: 0,
maxHeight: double.infinity,
alignment: Alignment.topCenter,
child: SizeReportingWidget(
onSizeChange: (size) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
setState(() {
if ((size.height - widget.defaultHeight).abs() > 10) {
_heights[index] = size.height;
}
});
}
});
},
child: item,
),
);
}

void _updatePage() {
final newPage = _pageController?.page?.round();
if (_currentPage != newPage) {
// Use post-frame callback to defer state update
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
setState(() {
_currentPage = newPage ?? _currentPage;
});
}
});
}
}
}

class SizeReportingWidget extends StatefulWidget {
final Widget child;
final ValueChanged<Size> onSizeChange;

const SizeReportingWidget({
required this.child,
required this.onSizeChange,
super.key,
});

@override
_SizeReportingWidgetState createState() => _SizeReportingWidgetState();
}

class _SizeReportingWidgetState extends State<SizeReportingWidget> {
Size? _oldSize;

@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_notifySize();
});
return widget.child;
}

void _notifySize() {
if (mounted) {
final size = context.size;
if (_oldSize != size) {
_oldSize = size;
if (size != null) widget.onSizeChange(size);
}
}
}
}
Loading

0 comments on commit 4ae7fc0

Please sign in to comment.