Flutter + Source Generation: The birth of a Magical Widget [Part 2]

if it is set to false, then no further code will be generated.

However, if it is set to true, then an inherited widget will be generated for you.

The inherited widget is called MagicalWidget, and it contains a MagicalBloc called magicalBloc as an instance variable.

The MagicalWidget is created to your convenience so you could easily propagate the magicalBloc in the widget tree.

You can always get this BLoC by calling the static of() method of the InheritedWidget class.

Take a look back at the enum that we just written, it is only a simple enum, yet it took us many lines to discuss what it generated.

So, we agree it is simple, but it is powerful.

Now, we will continue the example to demonstrate this power.

In the pages folder, I only have one dart file called mypage.

dart, it contains the following:import 'package:example/src/widgets/Button_one.

dart';import 'package:example/src/widgets/button_two.

dart';import'package:example/src/widgets/txt_field_one.

dart';import'package:example/src/widgets/txt_field_two.

dart'; import'package:flutter/material.

dart';import 'package:example/src/blocs/first_page_bloc.

dart' as first_bloc;class MyPage extends StatelessWidget{@overrideWidget build(BuildContext context) {return first_block.

MagicalWidget( child: Column( mainAxisAlignment:MainAxisAlignment.

spaceEvenly, crossAxisAlignment:CrossAxisAlignment.

center, children[ BtnOneWidget(), BtnTwoWidget(), TxtFieldOneWidget(), TxtFieldTwoWidget(),]));}}This file contains a column, which in turn contains the different widgets.

These widgets exist in the widgets folder as shown in the picture.

 The main thing to notice in this file is that we wrapped the column with the automatically created MagicalWidget.

Now all our widgets within this tree will have access to the MagicalBloc provided by this MagicalWidget, as we will see next.

Also pay attention to the import of the first_page_bloc.

dart, I gave it an alias, and this is a good practice to follow.

The magical widget package will always create classes with the same name (they are MagicalController, MagicalBloc, and MagicalWidget), so aliases is the only way to differentiate between them in case you imported more than one magical widget into the same file.

Now we develop our application logic.

 Going back to our enum definition, we want button one to be enabled by default, button two to be disabled, text field one to have empty string, and text field two to have the word Magic by default.

We also want:When we click on button one we enable button twoWhen we click on button two we change the text of input field oneThe text of button two is what we write in input field twoWhen we write enable in text field one button one is enabled but if we write disable then it is disabledThese are just dummy interactions to demonstrate the package.

The state and the interactions will depend on your specific app.

Even thought, these are dummy and simple, but good luck writing the code from the start.

If you use setState() your code will easily become gibberish, and if you use any other state management technique, and want to write everything from the beginning, then you have a lot to do.

Let’s use the magical widget in our case, and let it do the trick.

Remember, we only wrote a simple enum for our BLoC implementation, nothing else.

In Flutter, whenever we want to declare our widget as a listener to a stream, we use the StreamBuilder widget, and this way, if something changed in the stream our widget will be updated.

 So let’s use StreamBuilder to declare our widgets as listeners on specific controls, and let’s change these controls as we stated above:btn_one.

dartLook at the code of button one: First as the good practice states, I imported first_page_bloc.

dart with an alias called first_bloc.

Second in the overridden build method we get the MagicalBloc instance that is provided by the MagicalWidget.

You almost always want to do this to get a reference to this instance.

 We can do this because we wrapped our main column widget with a MagicalWidget in first_page.

dart, you can recheck it above.

Next we want to return a button, but this button is enabled or disabled according to our enabbleFirstBtn control.

So this button is dependent on the stream and therefore we wrap it with a StreamBuilder.

 The generic type provided to this builder as you can see is first_bloc.

MagicalController.

This tells the builder that the events in the stream are of this type.

This is done on this line return StreamBuilder<first_bloc.

MagicalController>(.

);.

You need to provide a stream and a builder property for the StreamBuilder, initialData is optional.

However, you can use initialData and call an empty MagicalController constructor to initialize the state of your widgets.

The stream property is always bloc.

magicalStream, we are just referencing the stream of MagicalController.

Then your custom logic is written in the builder property.

You need to provide a function to this property.

This function takes two parameters (context, snapshot) and returns a widget.

context is apparently the context of the page, and snapshot gives you access to the current event in the stream through its data property (so snapshot.

data will return the current MagicalController in the stream).

In our code, we are getting the enableFirstBtn field from the MagicalController event, and we are using it on the onPressed property of the RaisedButton.

If enableFirstBtn is true, then we assign _onPressedFirstBtn to onPressed, otherwise we assign it null so it will disabled.

The _onPressedFirstBtn, and as you can see, sets the enableSecondBtn control to true, using the changeUIElement method.

 That’s it, now any change in enableFirstBtn will update the state of this widget immediately because it is wrapped in a StreamBuilder and the BLoC pattern is already implemented for you on the other side.

No need to add any other code.

I will add the code for the other widgets, and let you understand them.

I bet it is not that hard right now.

It is just a logical flow of events, as stated by our requirement above.

button_two.

dartThe second button depends on the stream to set it to enabled or disabled, but also to change its shown text.

When button two is pressed, it changes the text of input field one.

txt_field_two.

dartThe code for the first text field is shown above.

Basically, its text depends on the control txtField1Input.

This control is changed in the button_two.

dart when we press the button.

Then we test the entered text here, and if it is ‘disable’ then we disable button one, else if it is ‘enable’ then we enable button one.

And I am showing how to use changeUIElements as well to change more than one controls at a time.

txt_field_two.

dartI didn’t wrap the text field with a StreamBuilder, because its state depends on no changes coming from interactions with other widgets.

And if you noticed I used the magicValue variable to reference the current value of the stream, this is only useful when we first build the widget to have the default value which is ‘Magic’ as stated by our original enum.

Now if we run the app, and as expected we will have this screen:You can see that everything is as expected.

Every widget has it default value that we specified in our original enum.

The first button is enabled by default.

The second one is disabled and its text equals to the content of the second text field, which is ‘Magic’.

The first text field is empty, and as expected, the second text field is defaulted to ‘Magic’.

Now, and if you implemented the code all along, try to play with these widgets on your device or emulator and see if the interactions are as expected.

Check the GIF below, to see these interactions in action.

I hope this article gave you an idea about what you could do with the magical widget package.

With just a simple enum, you can now manipulate your UI widgets and manage their states with ease.

The number of controls in the enum depends on the complexity of your app, but you should add as much as you want, don’t use one control for two things for example, after all you are not writing the code yourself.

The type of interactions that you could achieve is really limitless, it is app to you what controls to include and how to use them.

At the end, I want to say that this package saved me a lot of time developing my app, and it will save you time too.

And if you didn’t see the first article, then go back and just check the last paragraph, to see how much a simple enum could offer you in a real app.

.. More details

Leave a Reply