The 2 keys to Modularization in Flutter: Classes and Parameter Methods
I want to give you a class that I think you're going to find very interesting since when we are developing in Flurer since it is strongly modular since everything in Flurer is a blessed widget we can as they say take for granted that our application that we are going to create even if it is small or medium sized will be modularized simply because of that well it is not rather the opposite in my judgment due to the ease with which we start adding codes with the Viso of Studio Code that we can easily start adding widgets we can fall as they say into that false well in that we break the modularization of our application and therefore you know we lose that capacity maintenance a worse code well what you understand obviously if this is for an end client and he is going to want as they say then to continue advancing in the application and he wants to present it to another developer obviously the other developer is not going to understand half a thing of what we are doing here because it is very complicated spaghetti code a little bit so as I told you eh there I gave you a small example of that.
First tip: Methods for small developments and classes for the rest
I already told you why, that is the context of all this, so here I am only going to give you two pieces of advice, the first one is going to give you exactly when to use methods or when to use classes, for example a stateless widget, which are the ways in which we have to return content, in this case, for example, suppose here the publications one, we have a simple component like this, so I would use it, to give you the first piece of advice, I would use the methods to return a widget precisely if it is something extremely simple like this, here we have an image, we have some text, over here we have the title, and there I would perfectly use a method to easily reuse it.
Class modularization
The widget we are going to analyze is the web viewer of the Academia app for Flutter/Android, which has the following elements, that is, the first thing we must do in these cases is functionally divide what we want to implement, in this example, a native viewer with options for selecting text and creating notes.
- Translate HTML: HtmlToWidgets
- Select text and display a bubble: CustomTextSelectionOverlay
- Create notes: CreateBookNoteForm
These are the modules in our app, and they need to receive only the data needed. Here's my class-based implementation:
HtmlToWidgets(
html: bookSectionModel.content,
sizeFactText: sizeText,
htmlNoteBook: htmlNoteBook,
callback: noteAdd,
setKeys: setKeys,
),
***
class HtmlToWidgets extends StatelessWidget {
***
/*const*/
HtmlToWidgets({
super.key,
required this.html,
required this.sizeFactText,
this.callback,
this.setKeys,
this.htmlNoteBook = const {},
});
***
_hightlightText(
" ${element.text}",
element.attributes['id'] ?? idChild2,
16 * sizeFactText,
context,
htmlNoteBook[element.attributes['id'] ?? idChild2],
'SUSE'
),
***
Widget _hightlightText(
String text,
String idHTML,
double fontSize,
BuildContext context,
HtmlNoteModel? htmlNoteBook, [
String fontFamily = 'IBMPlexMono',
]) {
***
if (tokenText == "") {
return CustomTextSelectionOverlay(
text,
tokenText,
idHTML,
fontSize,
callback!,
// () => callback(id, tokenText),
);
}
}
}
class CustomTextSelectionOverlay extends StatefulWidget {
***
}
class _CustomTextSelectionOverlayState
extends State<CustomTextSelectionOverlay> {
***
child: CreateBookNoteForm(saveNote),
As you can see in the code above, the important thing is to see the class signatures. It receives what it needs to perform its work, and for the final operations, it is sent to the function as a parameter.
An example of a flawed implementation, where a function receives parameters it will NOT use directly:
List<Widget> showSectionBookByHTML(
String html,
String tokenUser,
int bookSectionId,
Function noteAdd,
BuildContext context, [
Map<String, BookSectionNoteModel> bookSectionNotesModel = const {},
double sizeText = 1.0,
]) {
In the previous example, which is a flawed implementation, not using classes not only complicates management by having a bunch of classes at the same level with different names, but also receives parameters that aren't specifically used by the function; for example, the user notes function.
What the function does is display the HTML content, but the user comments in the notes are NOT a direct feature of the viewer; they are an add-on or optional feature of a native viewer.
Thanks to modularization with classes, we pass the following methods as parameters:
HtmlToWidgets(
html: bookSectionModel.content,
sizeFactText: sizeText,
htmlNoteBook: htmlNoteBook,
callback: noteAdd,
setKeys: setKeys,
),