fix(mobile): resolve 8 Android debug errors and Kotlin migration

- Migrate to Flutter built-in Kotlin: set builtInKotlin=true and
  newDsl=true in gradle.properties; remove id(kotlin-android) from
  app/build.gradle.kts. Eliminates the Kotlin Gradle Plugin WARNING
  that caused 8 stderr lines in the VS Code Debug console.

- Fix DashboardController lifecycle in MobileShell: move controller
  creation from _buildBody() (called on every rebuild) to
  didChangeDependencies() so it is initialised once and reused.
  Prevents repeated data loads on every tab switch and resolves the
  203 skipped frames Choreographer warning.

- Fix KcSummaryCard RenderFlex overflow (8 cards, 19px each): reduce
  icon size 28->22px, switch spacing to xs, wrap value Text in
  FittedBox(scaleDown) with fontSize 28, use bodySmall + maxLines for
  label. Cards now fit within the GridView childAspectRatio:1.6
  constraint on all phone screen sizes.

All tests pass: 27 kell_mobile + 41 design_system.
This commit is contained in:
Mike Kell 2026-07-11 16:48:31 -04:00
parent 1023249592
commit 51ae862394
4 changed files with 48 additions and 14 deletions

View File

@ -1,6 +1,5 @@
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}

View File

@ -1,2 +1,6 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
# This builtInKotlin flag was added automatically by Flutter migrator
android.builtInKotlin=true
# This newDsl flag was added automatically by Flutter migrator
android.newDsl=true

View File

@ -30,8 +30,38 @@ class MobileShell extends StatefulWidget {
class _MobileShellState extends State<MobileShell> {
int _selectedIndex = 0;
/// Dashboard controller is created once and reused across rebuilds to avoid
/// re-triggering data loads on every [setState] call (e.g. tab switches).
late DashboardController _dashboardController;
static const _titles = ['Dashboard', 'Inventory', 'Products', 'Orders', 'More'];
@override
void didChangeDependencies() {
super.didChangeDependencies();
// Initialise the controller here (not in initState) because it needs
// KcAppScope which requires an inherited widget lookup.
if (!_dashboardControllerInitialised) {
final services = KcAppScope.of<MobileAppServices>(context);
_dashboardController = DashboardController(
GetDashboardSummary(
inventoryRepository: services.inventoryRepository,
productPublishingRepository: services.productPublishingRepository,
ordersRepository: services.ordersRepository,
),
);
_dashboardControllerInitialised = true;
}
}
bool _dashboardControllerInitialised = false;
@override
void dispose() {
_dashboardController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final config = KcAppScope.configOf<MobileAppServices>(context);
@ -86,15 +116,7 @@ class _MobileShellState extends State<MobileShell> {
switch (_selectedIndex) {
case 0:
return MobileDashboardPage(
controller: DashboardController(
GetDashboardSummary(
inventoryRepository: services.inventoryRepository,
productPublishingRepository: services.productPublishingRepository,
ordersRepository: services.ordersRepository,
),
),
);
return MobileDashboardPage(controller: _dashboardController);
case 1:
return MobileInventoryPage(
repository: services.inventoryRepository,

View File

@ -36,11 +36,20 @@ class KcSummaryCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: iconColor, size: 28),
const SizedBox(height: KcSpacing.sm),
Text(value, style: theme.textTheme.headlineMedium?.copyWith(fontSize: 32)),
Icon(icon, color: iconColor, size: 22),
const SizedBox(height: KcSpacing.xs),
Text(label, style: theme.textTheme.bodyMedium?.copyWith(color: KcColors.neutral)),
FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.centerLeft,
child: Text(value, style: theme.textTheme.headlineMedium?.copyWith(fontSize: 28)),
),
const SizedBox(height: KcSpacing.xs),
Text(
label,
style: theme.textTheme.bodySmall?.copyWith(color: KcColors.neutral),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
);