Open In App

Flutter – Custom Widgets

Improve
Improve
Like Article
Like
Save
Share
Report

We create Custom Widgets when we want a custom look and feel to our app, and we know that there will be a repetition of a particular widget. We can create the custom widget in a new dart file with all the codes and defining the parameters that we need in the constructor.

For more on how to split widgets, you can visit the article on Flutter – Splitting App into Widgets

Here we will be discussing an example of how to build a simple app by applying custom property to the widgets and making them separate from their own properties. We will be making a BMI Calculator App that takes height and weight to calculate the BMI of a person. To show how to use custom widgets we have also defined certain more things on the screen.

Let us start by cleaning up the main.dart file as:

Dart




import 'package:custom_widget_demo/home.dart';
import 'package:flutter/material.dart';
  
void main() {
  runApp(MyApp());
}
  
class MyApp extends StatelessWidget {
    
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'GFG Custom Widget Demo',
      theme: ThemeData.dark(),
      home: Home(),
    );
  }
}


As you can see that we have defined Home Screen to show all the components on the screen. Now as we have cleaned up the main file we will be creating a widgets directory and add three files namely custom_button.dart, custom_column.dart, and custom_container.dart files to this directory. We will be writing codes for our custom widgets in these files.

Starting with the custom_container.dart file we will be writing the following code:

Dart




import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
  
class CustomContainer extends StatelessWidget {
  CustomContainer(
      {@required this.child, this.height, this.width, this.onTap, this.color});
  final Function onTap;
  final Widget child;
  final double height;
  final double width;
  final Color color;
  
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        height: height,
        width: width,
        padding: EdgeInsets.all(12),
        decoration: BoxDecoration(
            color: color, borderRadius: BorderRadius.all(Radius.circular(8))),
        child: child,
      ),
    );
  }
}


In this custom widget, we have created a Stateless CustomContainer widget which is basically a container with a rounded corner and an onTap property which we have implemented using the GestureDetector widget. We have defined the properties of the widgets as the onTap which accepts a function, a child property that accepts a Widget, a height, width, and finally a color property. We will see the usage of this in the Home Widget that we will be defined later.

Moving on to our next Custom widget which we define in the custom_button.dart file that is simply a CustomButton widget which we define as follows –

Dart




import 'package:flutter/material.dart';
  
class CustomButton extends StatelessWidget {
  CustomButton({this.onTap, this.color = Colors.white30, this.icon});
  final Function onTap;
  final Color color;
  final IconData icon;
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        height: 50,
        width: 50,
        decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(30), color: color),
        child: Icon(icon),
      ),
    );
  }
}


We have defined simply a Stateless widget which acts as a simple button that uses a GestureDetector to detect the functions and also accepts an icon as the child to be displayed inside the button. It is a rounded button with a fixed height and width. We can alter the color if we need it, but it already has a custom color of translucent white. 

Coming to our last widget that is simply a CustomColumn which we define in the custom_column.dart file as:

Dart




import 'package:flutter/material.dart';
  
class CustomColumn extends StatelessWidget {
  CustomColumn({this.text, this.child});
  final String text;
  final Widget child;
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(
          text,
          style: TextStyle(fontSize: 18),
        ),
        child
      ],
    );
  }
}


This widget accepts a text and child as its properties displayed in the column.

Now that all the components are ready we are going to write the code that is going to display all of our contents on the screen. All of the code from now on is part of the home.dart file that we define in the lib

Dart




import 'package:custom_widget_demo/widgets/custom_button.dart';
import 'package:custom_widget_demo/widgets/custom_column.dart';
import 'package:custom_widget_demo/widgets/custom_container.dart';
import 'package:flutter/material.dart';
import 'dart:math';
  
enum g { m, f }


We import all of our custom widgets along with material and math.dart files. Apart from these we also created an enum for gender g.

Dart




class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}
  
class _HomeState extends State<Home> {
  final activeColor = Colors.white30;
  final inactiveColor = Colors.white12;
  g isSelected;
  int height = 160;
  int weight = 60;
  int age = 25;
  String bmi = '';


The Home widget is a stateful widget, and we have all the properties like the activeColor, inactiveColor, the isSelected for gender selection, the height, weight, and son on for BMI calculation.

Dart




@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('GFG Custom Widget'),
    ),
    body: SafeArea(
      child: Container(
        padding: EdgeInsets.all(12),
        child: Column(
          children: [
            Row(
              children: [
                Expanded(
                  child: CustomContainer(
                    color: isSelected == g.m ? activeColor : inactiveColor,
                    onTap: () {
                      setState(() {
                        isSelected = g.m;
                      });
                    },
                    child: Padding(
                      padding: const EdgeInsets.symmetric(vertical: 20.0),
                      child: Text(
                        'FEMALE',
                        textAlign: TextAlign.center,
                        style: TextStyle(fontSize: 18),
                      ),
                    ),
                  ),
                ),
                SizedBox(
                  width: 10,
                ),
                Expanded(
                  child: CustomContainer(
                    color: isSelected == g.f ? activeColor : inactiveColor,
                    onTap: () {
                      setState(() {
                        isSelected = g.f;
                      });
                    },
                    child: Padding(
                      padding: const EdgeInsets.symmetric(vertical: 20.0),
                      child: Text(
                        'MALE',
                        textAlign: TextAlign.center,
                        style: TextStyle(fontSize: 18),
                      ),
                    ),
                  ),
                ),
              ],
            ),


For the first part, we have defined a Scaffold under which we have defined an AppBar and then as a child, we have defined SafeArea. Now coming to the components we have defined a Column that contains all the components of the screen. The first child of the column is a row widget that contains two CustomContainer widgets with an onTap function to select the gender and change the color of the container as we do so. 

Dart




SizedBox(
        height: 10,
      ),
      CustomContainer(
        color: inactiveColor,
        height: 100,
        child: CustomColumn(
          text: 'HEIGHT $height cm',
          child: SliderTheme(
            data: SliderTheme.of(context).copyWith(
              activeTrackColor: Colors.white,
              thumbColor: Colors.green,
              overlayColor: Color(0x2900ff00),
              thumbShape:
                  RoundSliderThumbShape(enabledThumbRadius: 15.0),
              overlayShape:
                  RoundSliderOverlayShape(overlayRadius: 25.0),
            ),
            child: Slider(
              value: height.toDouble(),
              min: 120.0,
              max: 220.0,
              onChanged: (double newValue) {
                setState(() {
                  height = newValue.floor();
                });
              },
            ),
          ),
        ),
      ),


After giving some space we have again defined a CustomContainer with the inactive color with a CustomColumn which accepts the text as Height with dynamically changing heights with the help of the slider that we defined. We have provided custom properties to the slider to look and feel according to our app.

Dart




SizedBox(
        height: 10,
      ),
      Row(
        children: [
          Expanded(
            child: CustomContainer(
              color: inactiveColor,
              child: CustomColumn(
                text: 'WEIGHT $weight',
                child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      CustomButton(
                        onTap: () {
                          setState(() {
                            weight = weight - 1;
                          });
                        },
                        icon: Icons.arrow_downward,
                      ),
                      SizedBox(
                        width: 10,
                      ),
                      CustomButton(
                        onTap: () {
                          setState(() {
                            weight = weight + 1;
                          });
                        },
                        icon: Icons.arrow_upward,
                      )
                    ],
                  ),
                ),
              ),
            ),
          ),
          SizedBox(
            width: 10,
          ),
          Expanded(
            child: CustomContainer(
              color: inactiveColor,
              child: CustomColumn(
                text: 'AGE $age',
                child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      CustomButton(
                        onTap: () {
                          setState(() {
                            age = age - 1;
                          });
                        },
                        icon: Icons.arrow_downward,
                      ),
                      SizedBox(
                        width: 10,
                      ),
                      CustomButton(
                        onTap: () {
                          setState(() {
                            age = age + 1;
                          });
                        },
                        icon: Icons.arrow_upward,
                      )
                    ],
                  ),
                ),
              ),
            ),
          ),
        ],
      ),


Again After giving some space we defined a row with two CustomContainer which both accept a CustomColumn with the text as Weight and Age. Both of these containers has two buttons in a row as the child of the CustomColumn which we define. The functionality of these buttons is to increase or decrease the value of weight and age.

Dart




SizedBox(
        height: 10,
      ),
      Row(
        children: [
          Expanded(
            child: CustomContainer(
              onTap: () {
                setState(() {
                  bmi = '';
                });
              },
              width: double.infinity,
              child: Text(
                'CLEAR',
                style: TextStyle(
                  fontSize: 18,
                ),
                textAlign: TextAlign.center,
              ),
              color: activeColor,
            ),
          ),
          SizedBox(
            width: 10,
          ),
          Expanded(
            child: CustomContainer(
              onTap: () {
                double _bmi = weight / pow(height / 100, 2);
 
                setState(() {
                  bmi = _bmi.toStringAsFixed(1);
                });
              },
              width: double.infinity,
              child: Text(
                'GET BMI',
                style: TextStyle(
                  fontSize: 18,
                ),
                textAlign: TextAlign.center,
              ),
              color: Colors.green,
            ),
          ),
        ],
      ),


Here we have defined two buttons with the help of our CustomContainer. The first one is used to clear the output that is displayed and the other one shows the output on the screen.

Dart




SizedBox(
                height: 10,
              ),
              Expanded(
                child: CustomContainer(
                  width: double.infinity,
                  child: Column(
                    children: [
                      SizedBox(
                        height: 20,
                      ),
                      Text(
                        'YOUR BMI IS',
                        style: TextStyle(
                            fontSize: 18, fontWeight: FontWeight.bold),
                      ),
                      SizedBox(
                        height: 20,
                      ),
                      Text(
                        bmi,
                        style: TextStyle(
                            fontSize: 100, fontWeight: FontWeight.bold),
                      )
                    ],
                  ),
                  color: inactiveColor,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}


The last component of the App is also a CustomContainer which is used to display the calculated BMI on the Screen. This completes our app. Now you can run the app on your device.

Output:



Last Updated : 12 Mar, 2021
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads