form_ui_builder 0.1.0 copy "form_ui_builder: ^0.1.0" to clipboard
form_ui_builder: ^0.1.0 copied to clipboard

A Flutter package that helps you build dynamic and customizable user interfaces quickly and easily.

example/lib/main.dart

import 'dart:convert';

import 'package:example/bloc/main_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:form_ui_builder/form_ui_builder.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize the form_ui_builder package dependencies. buildFormLogicBundle
  // fails with a Failure until this has run.
  await setupServiceLocator();

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'form_ui_builder example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      home: BlocProvider(
        create: (_) => MainBloc(),
        child: const MyHomePage(title: 'form_ui_builder example'),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({required this.title, super.key});

  final String title;

  @override
  Widget build(BuildContext context) {
    return BlocConsumer<MainBloc, MainState>(
      listener: (context, state) {
        if (state.status == MainStatus.failure) {
          showSnackBar(context, state.errorMessage);
        }
      },
      builder: (context, state) {
        final isLoaded = state.formLogicBundle.parameterMap.isNotEmpty;

        return Scaffold(
          appBar: AppBar(
            backgroundColor: Theme.of(context).colorScheme.inversePrimary,
            title: Text(title),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: () =>
                      context.read<MainBloc>().add(const GetParameterEvent()),
                  child: Text(
                    isLoaded ? 'Reload schema' : 'Load schema',
                  ),
                ),
                const SizedBox(height: 16.0),
                ElevatedButton(
                  onPressed: isLoaded ? () => _openFullPage(context) : null,
                  child: const Text('Open as a full page'),
                ),
                const SizedBox(height: 16.0),
                ElevatedButton(
                  onPressed: isLoaded ? () => _openEmbedded(context) : null,
                  child: const Text('Open embedded (desktop layout)'),
                ),
                const SizedBox(height: 32.0),
                _SubmissionPreview(submission: state.submission),
              ],
            ),
          ),
        );
      },
    );
  }

  /// The ready-made full-page form.
  void _openFullPage(BuildContext context) {
    final mainBloc = context.read<MainBloc>();

    Navigator.of(context).push(
      MaterialPageRoute<void>(
        builder: (_) => BlocProvider(
          create: (_) => FormUiBuilderBloc(
            initialFormLogicBundle: mainBloc.state.formLogicBundle,
          ),
          child: Builder(
            builder: (innerContext) => FormUiBuilderScreen(
              title: 'Household survey',
              onSubmit: (submission) {
                mainBloc.add(FormSubmittedEvent(submission));
                Navigator.of(innerContext).pop();
              },
            ),
          ),
        ),
      ),
    );
  }

  /// The chrome-free view, embedded beside the host app's own panel.
  void _openEmbedded(BuildContext context) {
    final mainBloc = context.read<MainBloc>();

    Navigator.of(context).push(
      MaterialPageRoute<void>(
        builder: (_) => BlocProvider(
          create: (_) => FormUiBuilderBloc(
            initialFormLogicBundle: mainBloc.state.formLogicBundle,
          ),
          child: EmbeddedFormPage(mainBloc: mainBloc),
        ),
      ),
    );
  }
}

/// Shows [FormUiBuilderView] inside a host-owned desktop layout: the form on
/// the left, the host app's own panel and submit button on the right.
class EmbeddedFormPage extends StatelessWidget {
  const EmbeddedFormPage({required this.mainBloc, super.key});

  final MainBloc mainBloc;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Embedded in the host layout')),
      body: Row(
        children: [
          Expanded(
            flex: 2,
            child: FormUiBuilderView(
              // The host presents validation errors its own way rather than
              // letting the package throw up a SnackBar.
              onValidationError: (message) => showSnackBar(context, message),
              onSubmit: (submission) {
                mainBloc.add(FormSubmittedEvent(submission));
                Navigator.of(context).pop();
              },
            ),
          ),
          const VerticalDivider(width: 1.0),
          Expanded(
            child: Padding(
              padding: const EdgeInsets.all(16.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Host app panel',
                    style: Theme.of(context).textTheme.titleMedium,
                  ),
                  const SizedBox(height: 8.0),
                  const Text(
                    'The form on the left is FormUiBuilderView — no Scaffold, '
                    'no AppBar, no submit button of its own.',
                  ),
                  const Spacer(),
                  FilledButton(
                    onPressed: () => context.read<FormUiBuilderBloc>().add(
                      const SubmitForm(),
                    ),
                    child: const Text('Submit from the host'),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

/// Renders the payload the package handed back.
class _SubmissionPreview extends StatelessWidget {
  const _SubmissionPreview({required this.submission});

  final FormSubmission? submission;

  @override
  Widget build(BuildContext context) {
    final submission = this.submission;
    if (submission == null) {
      return const Text('No form submitted yet.');
    }

    const encoder = JsonEncoder.withIndent('  ');
    final hiddenCount = submission.fields.where((f) => !f.isVisible).length;

    return ConstrainedBox(
      constraints: const BoxConstraints(maxWidth: 520.0, maxHeight: 260.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            '${submission.values.length} answers keyed by database_param '
            '($hiddenCount hidden fields excluded):',
            style: Theme.of(context).textTheme.labelLarge,
          ),
          const SizedBox(height: 8.0),
          Expanded(
            child: SingleChildScrollView(
              child: SelectableText(encoder.convert(submission.toJson())),
            ),
          ),
        ],
      ),
    );
  }
}
0
likes
160
points
148
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A Flutter package that helps you build dynamic and customizable user interfaces quickly and easily.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

equatable, flutter, flutter_bloc, fpdart, get_it, synchronized

More

Packages that depend on form_ui_builder