flutter_mediator 2.2.5 copy "flutter_mediator: ^2.2.5" to clipboard
flutter_mediator: ^2.2.5 copied to clipboard

Flutter mediator is a reactive state management package base on the InheritedModel with automatic aspect management.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:flutter_i18n/loaders/decoders/json_decode_strategy.dart';
// ignore: depend_on_referenced_packages
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_mediator/mediator.dart';

import 'models/list_model.dart';
import 'models/my_model.dart';
import 'models/setting_model.dart';
import 'pages/info_page.dart';
import 'widgets/bottom_navigation_controller.dart';
import 'widgets/widget_extension.dart';

const double Width = 150;

final globalInt = globalWatch(1);
final globalList = globalWatch(<int>[]);

void main() {
  runApp(
    MultiHost.create3(
      MyModel(updateMs: 1000), // model that extends Pub
      ListModel(updateMs: 500), // model that extends Pub
      Setting(),
      child: MyApp(),
    ),
    // MultiHost.create( // Generic form
    //   hosts: [
    //     Host<MyModel>(model: MyModel(updateMs: 1000)),
    //     Host<ListModel>(model: ListModel(updateMs: 500)),
    //     Host<Setting>(model: Setting()),
    //   ],
    //   child: MyApp(),
    // ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Mediator Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: BottomNavigationWidget(
        pages: navPages,
        bottomNavItems: bottomNavItems,
        selectedColor: Colors.amber[800]!,
        backgroundColor: Colors.black54.withOpacity(0.5),
        selectedIndex: 0,
      ),
      // add flutter_i18n support
      localizationsDelegates: [
        FlutterI18nDelegate(
          translationLoader: FileTranslationLoader(
            // forcedLocale: ,
            // useCountryCode: true,
            // fallbackFile: 'en',
            basePath: 'assets/flutter_i18n',
            decodeStrategies: [JsonDecodeStrategy()],
          ),
          missingTranslationHandler: (key, locale) {
            print(
                '--- Missing Key: $key, languageCode: ${locale!.languageCode}');
          },
        ),
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
    );
  }
}

Widget buttonPage() {
  return Row(
    children: [
      Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          allSubscriber(),
          Pub.sub<MyModel>('int1AndStr1'),
          //int1AndStr1Subscriber(),
          fooSubscriber(),
          barSubscriber(),
          // inline subscriber
          'bar'.subModel<MyModel>((_, model) => Text('Bar is ${model.bar}')),
          starSubscriber(),
          str1Subscriber(),
          int1Subscriber(),
          chainReactSubscriber(),
          globalIntSubscriber(),
          globalListSubscriber(),
        ],
      ),
      Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          fooController(),
          barController(),
          bothController(),
          allController(),
          str1Controller(),
          int1Controller(),
          futureController(),
          globalIntController(),
          noController(),
          tick1(),
          tick2(),
          tick3(),
        ],
      ),
    ],
  );
}

Widget cardPage() {
  //* Original form:
  // return Subscriber<ListModel>(
  //   aspects: ListEnum.ListUpdate,
  //   create: (context, model) {
  //* Extension form:
  // return ListEnum.ListUpdate.subListModel((context, model) {
  return ListEnum.ListUpdate.subModel<ListModel>((context, model) {
    final data = model.data;
    return GridView.builder(
      itemCount: data.length,
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount:
              (MediaQuery.of(context).orientation == Orientation.portrait)
                  ? 5
                  : 10),
      itemBuilder: (context, index) {
        final item = data[index];
        return Card(
          color: item.color,
          child: GridTile(
            footer: Text(item.units.toString()),
            child: Text(item.item),
          ).padding(const EdgeInsets.all(7.0)),
        );
      },
    );

    //* List View
    // return ListView.builder(
    //   itemCount: data.length,
    //   itemBuilder: (context, index) {
    //     final item = data[index];
    //     return ListTile(
    //       title: Text(item.item),
    //       subtitle: Text(item.units.toString()),
    //     ).background(item.color);
    //   },
    // );
  });
}

//* Subscriber all aspects of the model
//* Get the frame aspects with `model.frameAspects`
Widget allSubscriber() {
  return null.subModel<MyModel>(
    (context, model) {
      final aspects = model.frameAspects;
      final str = aspects.isEmpty ? '' : '$aspects received';
      return Text(
        str,
        softWrap: true,
        textAlign: TextAlign.center,
      );
    },
  ).sizeBox(width: Width, height: 30);
}

//* Subscribe two aspects
Widget int1AndStr1Subscriber() {
  return
      // ['int1', 'star'].subModel<MyModel>(
      //* Rx automatic aspect form:
      rxSub<MyModel>(
    (context, model) => Text(
      'Int1 is ${model.int1} and Str1 is ${model.str1}',
      softWrap: true,
      textAlign: TextAlign.center,
    ),
  );
}

//* Subscribe in the original form.
Widget fooSubscriber() {
  return Subscriber<MyModel>(
    aspects: 'foo',
    create: (context, model) {
      print('foo build');
      return Text('Foo is ${model.foo}');
    },
  );
}

//* Subscriber in simple form.
Widget barSubscriber() {
  return 'bar'.subModel<MyModel>((context, model) {
    print('bar build');
    return Text('Bar is ${model.bar}');
  });
}

//* Subscriber with user extension.
Widget starSubscriber() {
  return 'star'.subMyModel((context, model) {
    print('star build');
    return Text('Star is ${model.star}');
  });
}

//* Subscriber in simple form.
Widget str1Subscriber() {
  return 'str1'.subMyModel((context, model) {
    print('str1 build');
    return Text('Str1 is ${model.str1}');
  });
}

//* Subscriber in the rx automatic aspect extension form.
Widget int1Subscriber() {
  //* Original form:
  // return Subscriber<MyModel>(
  //   aspects: 'int1',
  //   create:
  //* Extension form:
  // return 'int1'.subMyModel(
  // return 'int1'.subModel<MyModel>(
  //* Automatic aspect form:
  return rxSub<MyModel>((_, model) => Text('Int1 is ${model.int1}'));
  // //* Automatic aspect extension form:
  // return (context, model) {
  //   print('int1 build');
  //   return Text('Int1 is ${model.int1.value}');
  // }.rxSub<MyModel>();
}

//* Subscriber with the Global variable

Widget globalIntSubscriber() {
  return globalConsume(
    () => Text('Global Int: ${globalInt.value}'),
  );
}

Widget globalListSubscriber() {
  return globalConsume(
    () => Text('Global List len: ${globalList.value.length}'),
  );
  // return globalList.consume(
  //   () => Text('Global List len: ${globalList.value.length}'),
  // );
}

//* Future function for chainReactSubscriber
int httpResCounter = 0;
Future<int> _futureHttpTask() async {
  await Future.delayed(const Duration(milliseconds: 0));
  return ++httpResCounter;
}

//* Chain subscribe binding myModel.str1 with aspect 'chainStr1'.
Widget chainReactSubscriber() {
  return 'chainStr1'.subModel<MyModel>((context, model) {
    return FutureBuilder(
      future: _futureHttpTask(),
      initialData: httpResCounter,
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        Widget child;
        if (snapshot.hasData) {
          child = Text('str1 chain counter: $httpResCounter');
        } else {
          child = Text('str1 init counter: $httpResCounter');
        }
        return Center(child: child);
      },
    );
  });
}

//* Controller example
Widget fooController() {
  return Controller<MyModel>(
    create: (context, model) => ElevatedButton(
      child: const Text('Update foo'),
      onPressed: () => model.increaseFoo(),
    ),
  );
}

//* Controller example, using fat arrow expression
Widget barController() {
  return Controller<MyModel>(
    create: (context, model) => ElevatedButton(
      child: const Text('Update bar'),
      onPressed: () => model.increaseBar(),
    ),
  );
}

//* Controller example, update two variables.
Widget bothController() {
  return Controller<MyModel>(
    create: (context, model) => ElevatedButton(
      child: const Text('Update both'),
      onPressed: () => model.increaseBoth(),
    ),
  );
}

//* Controller example, update all variables.
Widget allController() {
  return Controller<MyModel>(
    create: (context, model) => ElevatedButton(
      child: const Text('Update all of MyModel'),
      onPressed: () => model.increaseAll(),
    ),
  );
}

Widget str1Controller() {
  return Controller<MyModel>(
    create: (context, model) => ElevatedButton(
      child: const Text('Update Str1'),
      onPressed: () => model.updateStr1(),
    ),
  );
}

Widget int1Controller() {
  return Controller<MyModel>(
    create: (context, model) => ElevatedButton(
      child: const Text('Update Int1'),
      onPressed: () => model.updateInt1(),
    ),
  );
}

Widget futureController() {
  return Controller<MyModel>(
    create: (context, model) => ElevatedButton(
      child: const Text('Future Int1:1sec'),
      onPressed: () => model.futureInt1(),
    ),
  );
}

Widget globalIntController() {
  return ElevatedButton(
      child: const Text('Update Global Int'),
      onPressed: () {
        globalInt.value++;
        // A. use `.ob` to notify the host to rebuild and then return the underlying object.
        globalList.ob.add(globalInt.value);
        // B. get the value then notify it.
        // globalList.value.add(globalInt.value);
        // globalList.notify();
      });
}

Widget noController() {
  return Controller<MyModel>(
    create: (context, model) => ElevatedButton(
      child: const Text('Update none'),
      onPressed: () => model.updateNone(),
    ),
  );
}

final _ColorRegistry tick1r = _ColorRegistry();
final _ColorRegistry tick2r = _ColorRegistry();
final _ColorRegistry tick3r = _ColorRegistry();

//* rxSub tick1 with timer update
Widget tick1() {
  // return 'tick1'.subMyModel(
  // return 'tick1'.subModel<MyModel>(
  return rxSub<MyModel>(
    (context, model) {
      // print('tick1 build');
      return _ColoredBox(
        color: tick1r.nextColor(),
        child: Text('tick1 is ${model.tick1}'),
      );
    },
  ).padding(const EdgeInsets.all(5.0)).sizeBox(width: Width, height: 50);
}

//* Subscribe tick2 with timer update
Widget tick2() {
  return 'tick2'
      .subModel<MyModel>(
        (context, model) {
          // print('tick2 build');
          return _ColoredBox(
            color: tick2r.nextColor(),
            child: Text('tick2 is ${model.tick2}'),
          );
        },
      )
      .padding(const EdgeInsets.all(5.0))
      .sizeBox(width: Width, height: 50);
}

//* Subscribe tick3 with timer update
Widget tick3() {
  return 'tick3'
      .subModel<MyModel>(
        (context, model) {
          // print('tick3 build');
          return _ColoredBox(
            color: tick3r.nextColor(),
            child: Text('tick3 is ${model.tick3}'),
          );
        },
      )
      .padding(const EdgeInsets.all(5.0))
      .sizeBox(width: Width, height: 50);
}

class _ColorRegistry {
  final List<Color> colors = [
    Colors.pink,
    Colors.red,
    Colors.orange,
    Colors.yellow,
    Colors.lightGreen,
    Colors.green,
    Colors.blue,
    Colors.indigo,
    Colors.purple,
  ];

  int _idx = 0;

  Color nextColor() {
    if (_idx >= colors.length) {
      _idx = 0;
    }
    return colors[_idx++];
  }
}

class _ColoredBox extends StatelessWidget {
  const _ColoredBox({Key? key, required this.color, required this.child})
      : super(key: key);

  final Color color;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      color: color,
      child: child,
    );
  }
}

//* Comment out Tick1 class.
// class Tick1 extends StatelessWidget {
//   final _ColorRegistry r = _ColorRegistry();
//   @override
//   Widget build(BuildContext context) {
//     return
//         // 'tick1'.subMyModel(
//         rxSub<MyModel>(
//       (context, model) {
//         // print('tick1 build');
//         return _ColoredBox(
//           color: r.nextColor(),
//           child: Text('tick1 is ${model.tick1}'),
//         );
//       },
//     ).padding(const EdgeInsets.all(5.0)).sizeBox(width: Width, height: 60);
//   }
// }

final bottomNavItems = [
  const BottomNavigationBarItem(
    label: 'Buttons',
    icon: Icon(Icons.lightbulb_outline),
    activeIcon: Icon(Icons.library_books),
    // backgroundColor: ,
  ),
  const BottomNavigationBarItem(
    label: 'ListView',
    icon: Icon(Icons.new_releases),
    activeIcon: Icon(Icons.payment),
    // backgroundColor: ,
  ),
  const BottomNavigationBarItem(
    label: 'Info',
    icon: Icon(Icons.info_outline),
    activeIcon: Icon(Icons.inbox),
    // backgroundColor: ,
  ),
];
final navPages = [buttonPage(), cardPage(), infoPage()];
6
likes
140
pub points
0%
popularity

Publisher

unverified uploader

Flutter mediator is a reactive state management package base on the InheritedModel with automatic aspect management.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (LICENSE)

Dependencies

flutter

More

Packages that depend on flutter_mediator