Skip to content

Syntax sugar optimizations to avoid nesting hell in Flutter.

License

Notifications You must be signed in to change notification settings

treeinfra/modifier

Repository files navigation

Modifier

Syntax sugar optimizations to avoid nesting hell in Flutter.

Examples:

Here is a Before code and an After code. Both their source codes are listed inside the child repositories in the example folder, where you can read the source code and run them to see whether they have the same result.

Before (115 lines):

import 'package:flutter/widgets.dart';

void main() {
  runApp(const App());
}

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

  @override
  Widget build(BuildContext context) {
    final media = MediaQueryData.fromView(View.of(context));
    return MediaQuery(
      data: media,
      child: Directionality(
        textDirection: TextDirection.ltr,
        child: MessageHandler(
          message: 'message',
          child: Builder(builder: (context) {
            final message = MessageHandler.of(context);
            return Center(
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Padding(
                    padding: const EdgeInsets.only(bottom: 50),
                    child: Text(message),
                  ),
                  GestureDetector(
                    onTap: () {
                      MessageHandler.update(context, (message) => '$message.');
                    },
                    child: const Text('Click me to append a dot'),
                  ),
                ],
              ),
            );
          }),
        ),
      ),
    );
  }
}

class MessageHandler extends StatefulWidget {
  const MessageHandler({
    super.key,
    required this.message,
    required this.child,
  });

  final String message;
  final Widget child;

  static String? maybeOf(BuildContext context) =>
      context.dependOnInheritedWidgetOfExactType<_InheritedMessage>()?.message;

  static String of(BuildContext context) {
    final message = maybeOf(context);
    assert(message != null, 'cannot find $MessageHandler in context');
    return message!;
  }

  static void update(
      BuildContext context,
      String Function(String raw) updater,
      ) {
    final handler =
    context.dependOnInheritedWidgetOfExactType<_InheritedMessage>();
    if (handler != null) handler.update(updater(handler.message));
  }

  @override
  State<MessageHandler> createState() => _MessageHandlerState();
}

class _MessageHandlerState extends State<MessageHandler> {
  late String _message = widget.message;

  void update(String message) {
    if (_message != message) {
      setState(() {
        _message = message;
      });
    }
  }

  @override
  void didUpdateWidget(covariant MessageHandler oldWidget) {
    super.didUpdateWidget(oldWidget);
    update(widget.message);
  }

  @override
  Widget build(BuildContext context) => _InheritedMessage(
    message: _message,
    update: update,
    child: widget.child,
  );
}

class _InheritedMessage extends InheritedWidget {
  const _InheritedMessage({
    required this.message,
    required this.update,
    required super.child,
  });

  final String message;
  final void Function(String message) update;

  @override
  bool updateShouldNotify(covariant _InheritedMessage oldWidget) =>
      message != oldWidget.message;
}

After (22 lines):

import 'package:flutter/widgets.dart';
import 'package:modifier/modifier.dart';

void main() {
  runApp(const App());
}

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

  @override
  Widget build(BuildContext context) => builder((context) => [
    context.findAndTrust<String>().asText.padding(bottom: 50),
    'Click me to append a dot'
        .asText
        .on(tap: () => context.update<String>((message) => '$message.'))
  ].asColumn)
      .center
      .handle('message')
      .ensureDirection(context)
      .ensureMedia(context);
}