generatePage function

List<GeneratedFile> generatePage(
  1. PageGenRequest req
)

Emits compilable stubs for a form page.

Implementation

List<GeneratedFile> generatePage(PageGenRequest req) {
  final feature = req.feature;
  final name = req.name;
  final featurePascal = _pascal(feature);
  final namePascal = _pascal(name);
  final mixinName = '$featurePascal${namePascal}Data';
  final pageName = '$namePascal${featurePascal}Page';
  final edit = req.editModel;

  final files = <GeneratedFile>[
    GeneratedFile(
      relativePath:
          'lib/features/$feature/controllers/${feature}_${name}_data.dart',
      contents: '''
import 'package:flutter/widgets.dart';
import 'package:flutter_page_kit/flutter_page_kit.dart';

mixin $mixinName<T extends StatefulWidget> on State<T>, PageData<T> {
${edit == null ? '' : '  $edit? get editing;\n'}
  late final title = text();

  @override
  List<Validatable> get validated => [title];

  List<PageAction> get actions => [
        PageAction(
          id: 'save',
          label: 'Save',
          busyKey: 'save',
          isPrimary: true,
          onPressed: submit,
        ),
      ];

  Future<void> submit() => run(
        key: 'save',
        action: () async {
          if (!await validateForm()) return;
          // TODO: call repository
        },
      );
}
''',
    ),
    GeneratedFile(
      relativePath: 'lib/features/$feature/pages/${name}_${feature}_page.dart',
      contents: '''
import 'package:flutter/material.dart';
import 'package:flutter_page_kit/flutter_page_kit.dart';
import '../controllers/${feature}_${name}_data.dart';
import '../widgets/${feature}_${name}_body.dart';

class $pageName extends StatefulWidget {
  const $pageName({super.key});

  @override
  State<$pageName> createState() => _${pageName}State();
}

class _${pageName}State extends State<$pageName>
    with PageData<$pageName>, $mixinName<$pageName> {
  @override
  Widget build(BuildContext context) {
    return PageScope(
      data: this,
      child: FormPage(
        title: '$namePascal $featurePascal',
        actions: actions,
        failure: failure,
        child: const $featurePascal${namePascal}Body(),
      ),
    );
  }
}
''',
    ),
  ];

  for (var i = 1; i <= req.steps; i++) {
    files.add(
      GeneratedFile(
        relativePath:
            'lib/features/$feature/widgets/${feature}_${name}_step_$i.dart',
        contents: '''
import 'package:flutter/material.dart';

class $featurePascal${namePascal}Step$i extends StatelessWidget {
  const $featurePascal${namePascal}Step$i({super.key});

  @override
  Widget build(BuildContext context) {
    return const Center(child: Text('Step $i'));
  }
}
''',
      ),
    );
  }

  files.add(
    GeneratedFile(
      relativePath: 'lib/features/$feature/widgets/${feature}_${name}_body.dart',
      contents: '''
import 'package:flutter/material.dart';
${[
        for (var i = 1; i <= req.steps; i++)
          "import '${feature}_${name}_step_$i.dart';",
      ].join('\n')}

class $featurePascal${namePascal}Body extends StatelessWidget {
  const $featurePascal${namePascal}Body({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
${[
        for (var i = 1; i <= req.steps; i++)
          '        const $featurePascal${namePascal}Step$i(),',
      ].join('\n')}
      ],
    );
  }
}
''',
    ),
  );

  files.add(
    GeneratedFile(
      relativePath: 'test/${feature}_${name}_harness_test.dart',
      contents: '''
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('${_camel(feature)} ${_camel(name)} harness mounts', (tester) async {
    // TODO: mount generated page with PageHarness
    expect(true, isTrue);
  });
}
''',
    ),
  );

  return files;
}