flutter_view_controller 0.1.23 copy "flutter_view_controller: ^0.1.23" to clipboard
flutter_view_controller: ^0.1.23 copied to clipboard

Pacote para construção de projetos flutter com padrão reativo.

LogoType

Complexes interfaces with no effort at all.
Reusable componentes with full control of its states ans layout.
Bidirectional comunication between parents and child easily.
UI update direct from your business logics.
All of it and much more with a elegant and simple sintaxe.


Table of Contents #

#



Hierarchy

The controller is composed of child controllers, which are passed as arguments to their corresponding child views.

app.dart

class AppController extends Controller {
  ChildController child = ChildController();

  @override
  onInit() {}

  @override
  onClose() {}
}

class AppView extends ViewOf<AppController> {
  const AppView({required super.controller});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ChildView(controller: controller.child)
        )
    );
  }
}



child.dart

class ChildController extends Controller {
  @override
  onInit() {}

  @override
  onClose() {}
}

class ChildView extends ViewOf<ChildController> {
  const ChildView({required super.controller});

  @override
  Widget build(BuildContext context) {
    return Text("Child");
  }
}

#




View

The view part is responsible for building the layout. By default, every view is static. Reactivity is achieved through notifiers.

It can be used for the entire page or just a component.
Every view needs a controller, even if it's not used

view.dart


class ExampleView extends ViewOf<ExampleController> {
  const ExampleView({required super.controller});

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 30,
      width: 30,
      color: Colors.red,
      child: ...
    );
  }
}

#




Controller

The Controller class is responsible for the behavior of the view. It contains all the actions and attributes of the view, and every view has direct access to its corresponding controller. The controller includes methods and properties.

controller.dart

import 'package:flutter_view_controller/flutter_view_controller.dart';

class ExampleController extends Controller {
  ButtonController _buttonController;
  int counter = 0;
  
  @override
  onInit(){
    _configButtonController();
  }

  _configButtonController(){
    _buttonController.onClick.then(incrementCounter);
  }

  incrementCounter(){
    counter++;
  }

  @override
  onClose(){}
}

#




Notifier

Notifiers are fundamental to the state management of a Flutter View Controller component. They are responsible for notifying the view about property changes. If you have a data that will affect the layout, you need to use a Notifier.

You need to declare the Notifier in the controller, and all Notifiers must have a type.

View updates #

You listen to it's changes with method "show" in the view.
To change the value of the notifier, you just need to put a new value in it.

app.dart

import 'package:flutter_view_controller/flutter_view_controller.dart';

class AppController extends Controller {

  Notifier<String> message = Notifier("Default Message");
  Notifier<int> number = Notifier(0)

  @override
  onInit(){
    message.value = "New message";
    number.value++;
  }

  @override
  onClose(){}
}

class AppView extends View<AppController> {
  AppView({required super.controller});

  @override
  Widget build(BuildContext context) {
    return Column(children:[
      Container(
        child: controller.message.show((snapshotMessage)=> Text(snapshotMessage))
      ),
      Container(
        child: controller.number.show((snapshotNumber)=> Text(snapshotNumber.toString()))
      ),
    ]);
  }
}



Listen to change inside the controllers #

You can also listen to changes inside controller.

controller.dart

import 'package:flutter_view_controller/flutter_view_controller.dart';

class MessageController extends Controller {
  Notifier<String> message = Notifier('Default Message');

  @override
  onInit(){
    message.listen((snapshotMessage)=>  print(snapshotMessage));
  }

  @override
  onClose(){}
}



Connecting notifiers #

You can also connect to another Notifier to propagate the change. This is very useful when you have complex structures.

child.dart

import 'package:flutter_view_controller/flutter_view_controller.dart';

class ChildController extends Controller {

  Notifier<String> message = Notifier("Default Message");

  @override
  onInit(){
    //This will take effect when parent message updates
    message.listen((newMessage) => print(newMessage));
  }

  @override
  onClose(){}
}

parent.dart

import 'package:flutter_view_controller/flutter_view_controller.dart';
import 'child.dart';

class ParentController extends Controller {
  ChildController child = ChildController();
  Notifier<String> message = Notifier("Default Message");

  @override
  onInit(){
    message.connect(child.message);
    message.value = "New message";
  }

  @override
  onClose(){}
}



Notifing lists #

You can also use a NotifierList to create reactive lists in your layout. NotifierList is still in the early stages, but it has the necessary methods to change its reactivity.

app.dart

import 'package:flutter_view_controller/flutter_view_controller.dart';

class AppController extends Controller {

  NotifierList<String> messages = NotifierList();

  @override
  onInit(){}

  addMessage(){
    messages.add('Message');
  }

  @override
  onClose(){}
}

class AppView extends View<AppController> {
  AppView({required super.controller});

  @override
  Widget build(BuildContext context) {
    return controller.messages.show((messages) => 
      Column(
        children:[
          GestureDetector(
            onTap: controller.addMessage,
            child: Container(
                child: Text('Add message')
            ),
          ),
          ...messages.map((message)=> Text(message))
          )
        ]
      ),
    );
  }
}

Note that the show method returns a single Widget because it always returns a NotifierListener. Therefore, to use it in a list, you need to wrap the entire list with it. After that, you can spread the items as you like.

Notifiers without value #

If you want to notify some event without changing any value, you can use a NotifierTicker. It can generate a pulse with all the capabilities of a Notifier.

However, instead of adding a new value to it, it comes with a tick() method that triggers it. Also, its show method does not receive any value

app.dart

import 'package:flutter_view_controller/flutter_view_controller.dart';

class AppController extends Controller {

  String message = 'Default Message',
  NotifierTicker ticker = NotifierTicker();

  @override
  onInit(){}

  pulse(){
    message = 'New Message';
    ticker.tick();
  }

  @override
  onClose(){}
}

class AppView extends View<AppController> {
  AppView({required AppController controller}) : super(controller: controller);

  @override
  Widget build(BuildContext context) {
    return controller.ticker.show(() => 
      GestureDetector(
        onTap: controller.pulse,
        child: Container(
            child: Text(controller.message)
        ),
      ),
    );
  }
}

The NotifierTicker is used in the built-in update method that all controllers have. It is used to refresh all the pages controlled by it.

#




Theme

With Flutter View Controller, you can easily develop dynamic themes using a GlobalState. A GlobalState is a map of objects whose changes have an effect globally on your aplication during runtime. You can define a theme with a simple abstract class.

Defining themes #

Here is an example of how to create a theme file for your app or component.

contract.dart

abstract class ThemeContract{
  late Color backgroundColor;
  late Color foregroundColor;
}

light.dart

class LightTheme implements ThemeContract{
  Color backgroundColor = Colors.white;
  Color foregroundColor = Colors.grey;
}

dark.dart

class LightTheme implements ThemeContract{
  Color backgroundColor = Colors.black;
  Color foregroundColor = Colors.blueGrey;
}

main.dart

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

  @override
  Widget build(BuildContext context) {
    GlobalState<ThemeContract>().register(LightTheme());
    ...
    return MaterialApp(
      ...
    );
  }
}



Consuming themes #

You can consume any properties of your theme in any view or controller like this:

app.dart

import 'package:flutter_view_controller/flutter_view_controller.dart';

class AppController extends Controller {

  Color consumedFromController;

  @override
  onInit(){
    consumedFromController = GlobalState<ThemeContract>().current.backgroundColor;
  }

  @override
  onClose(){}
}

class AppView extends View<AppController> {
  AppView({required AppController controller}) : super(controller: controller);

  @override
  Widget build(BuildContext context) {
    return Container(
        color: GlobalState<ThemeContract>().current.foregroundColor,
        ...
    );
  }
}



Changing themes #

To change the state of the GlobalState, you just need to update the corresponding property.

app.dart

import 'package:flutter_view_controller/flutter_view_controller.dart';

...

class AppView extends View<AppController> {
  AppView({required AppController controller}) : super(controller: controller);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: (){
        if(GlobalState<ThemeContract>().current is LightTheme){
          GlobalState<ThemeContract>().current = DarkTheme();
        }else{
          GlobalState<ThemeContract>().current = LightTheme();
        }
      },
      child: Container(
        color: GlobalState<ThemeContract>().current.foregroundColor,
        ...
    );
  }
}



Segmenting themes components #

You can use as many GlobalStates as you want to do different things.

main.dart

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

  @override
  Widget build(BuildContext context) {
    GlobalState<ThemeContract>().register(LightTheme());
    GlobalState<FontContract>().register(ComicsFonts());
    GlobalState<ShapeContract>().register(RoundedComponents());

    ...
    return MaterialApp(
      ...
    );
  }
}




ScreenSize

As a way to simplify the calculation of widget sizes, Flutter View Controller comes with a built-in property in its views. You can access the screen width and height at any time and even calculate a percentage of the view to dimension your components.

app.dart

import 'package:flutter_view_controller/flutter_view_controller.dart';

class AppView extends View<AppController> {
  AppView({required AppController controller}) : super(controller: controller);

  @override
  Widget build(BuildContext context) {
    return Container(
      height: size.height(55.2),
      width: size.width(10),
      ...
    )
  }
}

In this example we built a Container with 55.2% of screen height and 10% of screen width.

Contribute #

If you have some improvement or correction to make, please feel free to open an issue or pull request on the github project. All feedback are very welcome.

Buy Me A Coffee
3
likes
120
pub points
67%
popularity

Publisher

unverified uploader

Pacote para construção de projetos flutter com padrão reativo.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (LICENSE)

Dependencies

flutter, plug, sizer_plus

More

Packages that depend on flutter_view_controller