Startup Namer - Part 2

What you’ll learn

The following animated GIF shows how the app works at the completion of part 2:

6556f8b61acd6a89

Stateless widgets are immutable, meaning that their properties can’t change—all values are final.

Stateful widgets maintain state that might change during the lifetime of the widget. Implementing a stateful widget requires at least two classes: a StatefulWidget that creates an instance of a State class. The StatefulWidget object is, itself, immutable and can be thrown away and regenerated, but the Stateobject persists over the lifetime of the widget.

In this task, you’ll add a stateful widget, RandomWords, which creates its State class, _RandomWordsState. You’ll then use RandomWords as a child inside the existing MyApp stateless widget.

It can go anywhere in the file outside of MyApp, but the solution places it at the bottom of the file. In lib/main.dart, position your cursor after all of the code, enter Return a couple times to start on a fresh line. In your IDE, start typing stful. The editor asks if you want to create a Stateful widget. Press Return to accept. The boilerplate code for two classes appears, and the cursor is positioned for you to enter the name of your statefull widget.

As you can see in the code below, the RandomWords widget does little else beside creating its State class.

Once you’ve entered RandomWords as the name of the stateful widget, the IDE automatically updates the accompanying State class, naming it _RandomWordsState. By default, the name of the State class is prefixed with an underscore. Prefixing an identifier with an underscore enforces privacy in the Dart language and is a recommended best practice for State objects.

The IDE also automatically updates the state class to extend State<RandomWords>, indicating that you’re using a generic State class specialized for use with RandomWords. Most of the app’s logic resides here⁠—it maintains the state for the RandomWords widget. This class saves the list of generated word pairs, which grows infinitely as the user scrolls and, in next task, favorites word pairs as the user adds or removes them from the list by toggling the heart icon.

Both classes now look as follows:

class RandomWords extends StatefulWidget {
  @override
  _RandomWordsState createState() => _RandomWordsState();
}

class _RandomWordsState extends State<RandomWords> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

Replace return Container(); with the following two lines:

class _RandomWordsState extends State<RandomWords> {
  @override                                  
  Widget build(BuildContext context) {
    final wordPair = WordPair.random();      // NEW
    return Text(wordPair.asPascalCase);      // NEW
  }                                         
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random();  // DELETE

    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),
        ),
        body: Center(
          //child: Text(wordPair.asPascalCase), // REPLACE with... 
          child: RandomWords(),                 // ...this line
        ),
      ),
    );
  }
}

Tip: If you see a warning on a hot reload that you might need to restart the app, you should consider restarting the app. It might be a false positive, but restarting ensures that your changes are reflected in the app’s UI.

In the next step, you’ll expand _RandomWordsState to generate and display a list of word pairings. As the user scrolls, the list (displayed in a ListView widget) grows infinitely. The builder factory constructor in ListView allows you to lazily build a list view on demand.

Add a _suggestions list for saving suggested word pairings. Also, add a _biggerFont variable for making the font size larger.

class _RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];                 // NEW
  final _biggerFont = const TextStyle(fontSize: 18); // NEW
  ...
}

Next, you’ll add a _buildSuggestions() function to the _RandomWordsState class. This method builds the ListView that displays the suggested word pairing.

The ListView class provides a builder property, itemBuilder, that’s a factory builder and callback function specified as an anonymous function. Two parameters are passed to the function—the BuildContext and the row iterator, i. The iterator begins at 0 and increments each time the function is called, once for every suggested word pairing. This model allows the suggestion list to continue growing as the user scrolls.

In the _RandomWordsState class, add the following function, deleting the comments, if you prefer:

  Widget _buildSuggestions() {
    return ListView.builder(
      padding: const EdgeInsets.all(16),
      // The itemBuilder callback is called once per suggested 
      // word pairing, and places each suggestion into a ListTile
      // row. For even rows, the function adds a ListTile row for
      // the word pairing. For odd rows, the function adds a 
      // Divider widget to visually separate the entries. Note that
      // the divider may be difficult to see on smaller devices.
      itemBuilder: (BuildContext _context, int i) {
        // Add a one-pixel-high divider widget before each row 
        // in the ListView.
        if (i.isOdd) {
          return Divider();
        }

        // The syntax "i ~/ 2" divides i by 2 and returns an 
        // integer result.
        // For example: 1, 2, 3, 4, 5 becomes 0, 1, 1, 2, 2.
        // This calculates the actual number of word pairings 
        // in the ListView,minus the divider widgets.
        final int index = i ~/ 2;
        // If you've reached the end of the available word
        // pairings...
        if (index >= _suggestions.length) {
          // ...then generate 10 more and add them to the 
          // suggestions list.
          _suggestions.addAll(generateWordPairs().take(10));
        }
        return _buildRow(_suggestions[index]);
      }
    );
  }

The _buildSuggestions function calls _buildRow once per word pair. That function displays each new pair in a ListTile, which allows you to make the rows more attractive in the next task.

  Widget _buildRow(WordPair pair) {
    return ListTile(
      title: Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
    );
  }

Change it to use _buildSuggestions(), rather than directly calling the word-generation library. ( Scaffoldimplements the basic Material Design visual layout.)

  @override
  Widget build(BuildContext context) {
    //final wordPair = WordPair.random(); // Delete these... 
    //return Text(wordPair.asPascalCase); // ... two lines.

    return Scaffold (                     // Add from here... 
      appBar: AppBar(
        title: Text('Startup Name Generator'),
      ),
      body: _buildSuggestions(),
    );                                      // ... to here.
  }
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Startup Name Generator',
      home: RandomWords(),
    );
  }
IOS Android
ae47ef0ac2f492b8 df2b3cb779e0020e

Challenge

Now that you’ve halfway built the app, it’s time to customize it to make it your own🤗.

As a Challenge, I want you to Incorporate Custom Fonts into Your Startup Namer.

You can download fancy fonts from https://fonts.google.com/ Hinds:-

  1. Import the font files
  2. Declare the font in the pubspec
  3. Use the font.

You can customize it additionally by adding fontcolors, backgroundcolors , or anything which you think will make your app look good😇.