Flutter Create a New Custom Widget

Let’s create a new widget that will wrap this question text.

Text(
      questions[_questionIndex],
      ),

To create such a new widget, we can simply add a new class. Now we can do this in the same file (main.dart) we previously worked in but a good convention rule is to only have one widget per file. There are rare exceptions to that rule, for example we have two widgets that really only work together and we don’t really plan on reusing a widget in other files but generally, you want to have one widget per file.

Therefore, here I’ll add a new file next to the main.dart file and I’ll name it question.dart and this should hold my question widget. 

 

//FileName: question.dart
import 'package:flutter/material.dart';

class Question extends StatelessWidget {
  /*
  * final keywords tells dart that this value will never change after its
  * initialization here int he constructor
   */
  final String questionText;

  /* Lets have a constructor to initialize this widget class questionText
  *  Now the first positional argument passed to this constructor will be stored in
  *  questionText variable
   */
  Question(this.questionText);

  @override
  Widget build(BuildContext context) {
    return Text(questionText);
  } //build()
} //Question

Now, in our main.dart we need to import that Question widget by

/* ./ means look in the main folder where main.dart resides and then the
*  name of the file which you wana import
 */

import './question.dart';

It means that everything that is available in question.dart is now available in this file main.dart Please note that we didn’t had a leading underscore in Question class widget because that will make it private and we want to use it in main.dart file.

Now, in our main.dart file we’ll replace the Text() widget with Question() widget thus providing the same argument.

Question(
        questions[_questionIndex],
        ),

Question() constructor expects a String which we are passing from questions list based upon questionIndex.

Here is our main.dart file.

//File Name: main.dart
import 'package:flutter/material.dart';

/* ./ means look in the main folder where main.dart resides and then the
*  name of the file which you wana import
 */

import './question.dart';

/*
* The arrow function allows us to create a simplified function consisting of a single expression.
* We can omit the curly brackets and the return keyword
*/

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  /*
   * createState() is a method that takes no arguments but returns a State object
   * which is connected to a StatefulWidget
   */
  @override
  State<StatefulWidget> createState() {
    return _MyAppState();
  } //createState()
} //StatefulWidget

/* A leading underscore makes the property private, here leading underscore will turn
 * this public class in to private class. Now MyAppState class can only be used in
 * main.dart file, in our case in MyApp class.
 */
class _MyAppState extends State<MyApp> {
  var _questionIndex = 0;

  void _answerQuestion() {
    setState(() {
      _questionIndex++;
    });
    print(_questionIndex);
  }

  @override
  Widget build(BuildContext context) {
    var questions = [
      'What\'s your favorite color?',
      'What\'s your favorite animal?'
    ];
    return MaterialApp(
      /*
      * A Scaffold Widget provides a framework which implements the basic material design visual
      * layout structure of the flutter app.
       */
      home: Scaffold(
        appBar: AppBar(
          title: Text('First Flutter App'),
        ),
        body: Column(
          children: <Widget>[
            Question(
              questions[_questionIndex],
            ),
            ElevatedButton(
              child: Text('Answer1'),
              //onPressed takes a function
              onPressed: _answerQuestion,
            ),
            ElevatedButton(
              child: Text('Answer2'),
              onPressed: () => print('Answer2 chosen!'),
            ),
            ElevatedButton(
              child: Text('Answer3'),
              onPressed: () {
                //...multiline function body
                print('Answer3 chosen');
              },
            ),
          ],
        ),
      ),
    );
  } //build()
} //MyApp()

Now instead of using Text() widget we use our own widget Question where we also use a Text() widget. Of course for the simple restructuring, it is not useful, nonetheless it is a good idea to split your widgets or app in to smaller custom widgets that can help with performance. It can make our rebuilds more efficient and also it is easier for us to manage our code.

If our widget structure had complex logic and code as compared to a simple Text() widget then our main.dart file would be much simple and compact as soon as we move that in to a separate widget.

If we run our app it will have the same functionality and look as before.

Output

Output – First Question

and if we press Answer1 it behaves the same as before.

Output – Second Question

Happy Coding!