Dev #3
@@ -1,3 +1,5 @@
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@@ -98,39 +100,83 @@ class HomeServiceSection extends StatelessWidget {
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
return GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
padding: const EdgeInsets.fromLTRB(16, 32, 16, 16),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 4,
|
||||
mainAxisSpacing: 12,
|
||||
crossAxisSpacing: 8,
|
||||
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: AppStyle.xs.copyWith(fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
],
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final itemWidth =
|
||||
(constraints.maxWidth -
|
||||
_horizontalPadding * 2 -
|
||||
_crossAxisSpacing * (_crossAxisCount - 1)) /
|
||||
_crossAxisCount;
|
||||
final labelStyle = _labelStyle(context, itemWidth);
|
||||
|
||||
return GridView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
_horizontalPadding,
|
||||
32,
|
||||
_horizontalPadding,
|
||||
16,
|
||||
),
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user