Home: samakan ukuran font label menu layanan

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
efrilm
2026-10-01 10:29:43 +07:00
co-authored by Claude Opus 5.5
parent e273785268
commit 5a8b020256
@@ -1,3 +1,5 @@
import 'dart:math' as math;
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@@ -98,39 +100,83 @@ class HomeServiceSection extends StatelessWidget {
context.router.push(route); context.router.push(route);
} }
static const double _horizontalPadding = 16;
static const int _crossAxisCount = 4;
static const double _crossAxisSpacing = 8;
/// Semua label memakai ukuran font yang sama: kalau label terpanjang tidak
/// muat di lebar sel, semuanya diperkecil bersama supaya tidak ada yang
/// kecil sendiri.
TextStyle _labelStyle(BuildContext context, double maxWidth) {
final style = DefaultTextStyle.of(
context,
).style.merge(AppStyle.xs.copyWith(fontWeight: FontWeight.w600));
final textScaler = MediaQuery.textScalerOf(context);
final textDirection = Directionality.of(context);
final widest = _menus
.map((menu) {
final painter = TextPainter(
text: TextSpan(text: menu.label, style: style),
textDirection: textDirection,
textScaler: textScaler,
maxLines: 1,
)..layout();
final width = painter.width;
painter.dispose();
return width;
})
.reduce(math.max);
if (widest <= maxWidth) return style;
return style.copyWith(fontSize: style.fontSize! * maxWidth / widest);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GridView.builder( return LayoutBuilder(
shrinkWrap: true, builder: (context, constraints) {
physics: const NeverScrollableScrollPhysics(), final itemWidth =
padding: const EdgeInsets.fromLTRB(16, 32, 16, 16), (constraints.maxWidth -
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( _horizontalPadding * 2 -
crossAxisCount: 4, _crossAxisSpacing * (_crossAxisCount - 1)) /
mainAxisSpacing: 12, _crossAxisCount;
crossAxisSpacing: 8, final labelStyle = _labelStyle(context, itemWidth);
mainAxisExtent: 76,
), return GridView.builder(
itemCount: _menus.length, shrinkWrap: true,
itemBuilder: (context, index) { physics: const NeverScrollableScrollPhysics(),
final menu = _menus[index]; padding: const EdgeInsets.fromLTRB(
return InkWell( _horizontalPadding,
borderRadius: BorderRadius.circular(AppValue.borderRadius), 32,
onTap: () => _open(context, menu), _horizontalPadding,
child: Column( 16,
children: [
const SizedBox(height: 4),
menu.icon.image(width: 44, height: 44, fit: BoxFit.contain),
const SizedBox(height: 6),
FittedBox(
fit: BoxFit.scaleDown,
child: Text(
menu.label,
maxLines: 1,
style: AppStyle.xs.copyWith(fontWeight: FontWeight.w600),
),
),
],
), ),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: _crossAxisCount,
mainAxisSpacing: 12,
crossAxisSpacing: _crossAxisSpacing,
mainAxisExtent: 76,
),
itemCount: _menus.length,
itemBuilder: (context, index) {
final menu = _menus[index];
return InkWell(
borderRadius: BorderRadius.circular(AppValue.borderRadius),
onTap: () => _open(context, menu),
child: Column(
children: [
const SizedBox(height: 4),
menu.icon.image(width: 44, height: 44, fit: BoxFit.contain),
const SizedBox(height: 6),
FittedBox(
fit: BoxFit.scaleDown,
child: Text(menu.label, maxLines: 1, style: labelStyle),
),
],
),
);
},
); );
}, },
); );