Flutter Course: 14.2 Asynchronous Method

Author: [Your Name]

Date: [Today’s Date]

The Importance of Asynchronous Programming

Asynchronous programming is an essential element in modern application development. In particular, asynchronous tasks are very important for improving user experience (UX) and increasing responsiveness. The asynchronous approach allows multiple tasks to be performed simultaneously, enabling the execution of other tasks while waiting for one task to complete. This enables users to use applications that are faster and smoother.

Asynchronous Programming in Flutter

Asynchronous programming in Flutter is primarily implemented through the Future and async/await keywords. Flutter is written in the Dart programming language and offers various features to facilitate asynchronous programming easily. Now, let’s take a look at how to use asynchronous functions in Flutter.

Future and Asynchronous Functions

Future is a class that represents the result of an asynchronous operation. A Future object represents a task that will complete in the future and is returned from an asynchronous function. An asynchronous function is defined with the async keyword, and it uses the await keyword internally to wait for the Future to complete.

Asynchronous Function Example


Future fetchData() async {
    await Future.delayed(Duration(seconds: 2));
    return "Data loading completed!";
}
            

The example above is an asynchronous function that returns the string “Data loading completed!” after waiting for 2 seconds.

Usage of await

The await keyword causes execution to wait until the Future is completed, enabling the sequential execution of asynchronous code. For example, here’s how to call the fetchData function above.

await Example


void main() async {
    print("Loading data...");
    String result = await fetchData();
    print(result);
}
            

The main function is also defined as async, and it uses await to wait for the result of the fetchData function. This allows the message “Loading data…” to be printed immediately, and after 2 seconds, “Data loading completed!” is printed.

Error Handling in Asynchronous Tasks

Error handling in asynchronous tasks is an important aspect. You can handle exceptions occurring in asynchronous functions using a try-catch block.

Error Handling Example


Future fetchWithError() async {
    throw Exception("An error occurred!");
}

void main() async {
    try {
        String result = await fetchWithError();
        print(result);
    } catch (e) {
        print("Error: ${e.toString()}");
    }
}
            

In the example above, the fetchWithError function throws an exception. The main function wraps the call to that function in a try-catch block to handle the error.

Various Methods to Manage Future

To manage asynchronous processing more effectively in Flutter, there are methods provided to execute multiple Futures simultaneously. Using the Future.wait method makes it possible to wait for multiple Futures at the same time.

Future.wait Example


Future fetchAllData() async {
    var first = fetchData();
    var second = fetchWithError();
    var results = await Future.wait([first, second]);

    print(results);
}

void main() async {
    try {
        await fetchAllData();
    } catch (e) {
        print("Error: ${e.toString()}");
    }
}
            

The fetchAllData function executes two Futures simultaneously and waits for the results. If the second Future throws an error, the catch block handles the error.

Utilizing Streams

A stream is used to handle the continuous flow of asynchronous data. It is useful in scenarios where data is generated continuously, such as websocket connections, file reading, or database queries. Streams handle a series of events asynchronously.

Stream Example


Stream numberStream() async* {
    for (int i = 0; i < 5; i++) {
        await Future.delayed(Duration(seconds: 1));
        yield i;
    }
}

void main() async {
    await for (var number in numberStream()) {
        print(number);
    }
}
            

The numberStream function creates a stream and outputs a number every second. The main function uses the await for keyword to access the data from the stream.

Best Practices in Asynchronous Programming

When engaging in asynchronous programming, it is important to follow several best practices:

  • Error Handling: Don’t forget to handle exceptions in asynchronous functions.
  • Combining Futures: Use Future.wait and Future.any to efficiently handle multiple tasks.
  • Using Streams: Consider streams when continuous data processing is needed.

Conclusion

Asynchronous programming in Flutter enhances user experience and enables concurrent task handling. By learning about various asynchronous processing techniques including Future, async/await, and streams, you can more easily manage complex applications. We hope this course helps you establish a foundation in asynchronous programming and apply it in real applications!

Flutter Course: Applying Responsive Layouts to Lotto App 13.6

1. Introduction

This tutorial explains how to apply responsive layouts to a lottery app using Flutter. Responsive design is essential for providing an optimal user experience across different screen sizes. As the shapes and sizes of mobile devices vary, effective layout settings are necessary.

2. Importance of Responsive Layouts

In mobile app development, responsive layouts are essential. Consideration must be given to how content displays across various resolutions and screen sizes, as this directly impacts the user experience. This section will explore the key principles and advantages of responsive layouts.

2.1. Flexible Grid System

A flexible grid system ensures that elements are appropriately arranged regardless of screen size. It describes how to properly structure columns and rows so that content can automatically adjust.

2.2. Utilizing Media Queries

This section covers how to adjust styles using media queries to fit various screen sizes. It includes examples of how to implement this concept in Flutter.

3. Developing a Lottery App with Flutter

This section describes the basic structure and functionality of the lottery app while setting the necessary configurations before applying a responsive layout. Features such as lottery number generation and result checking are added, including an explanation of how each component works.

3.1. Setting Up App Structure

Set up the Flutter project and install the necessary packages. Introduce the main files and directory structure.

3.2. Implementing the Lottery Number Generator

This section explains the process of implementing a feature that randomly generates lottery numbers. It also covers state management methods.

4. Applying Responsive Layouts

Now we transition to the key stage of applying responsive layouts. It details how to design layouts considering various screen sizes.

4.1. Using MediaQuery

This section explains how to use MediaQuery to check the current screen size and adjust the layout accordingly.

4.2. Utilizing LayoutBuilder

Discusses how to dynamically adjust the size of child Widgets using LayoutBuilder and the benefits of doing so.

4.3. Implementing Responsive Widgets

This section explains with examples how to utilize the flexible widgets provided by Flutter to support responsive layouts:

        
        Container(
            width: MediaQuery.of(context).size.width * 0.9,
            child: Column(
                children: [
                    // Various widgets...
                ],
            )
        )
        
        

5. Final Testing and Deployment

Testing will be conducted on various devices to ensure stability. This section will review the items to check during the testing process and precautions to take.

6. Conclusion

This tutorial covered various methods to apply responsive layouts to a lottery app using Flutter. You are now prepared to develop apps that function well on complex screens. Additionally, resources and tips for further enhancing responsive design will be provided.

Flutter Course: The Concept of Future

Hello! In this post, we will delve deeply into one of the important concepts in Flutter development, Future. Future is a concept that is useful in asynchronous programming, allowing you to perform other tasks while waiting for the response after requesting data. In this article, we will explain the concept of Future, how to use it, examples, and how it can be utilized in practice.

1. What is Asynchronous Programming?

Asynchronous programming is a programming approach that enables a program to process multiple tasks simultaneously. This is especially useful when performing time-consuming tasks such as network requests or file input/output. In traditional approaches, each task is executed sequentially, and the next task cannot begin until the current one is completed. However, using asynchronous programming allows for other tasks to be performed while waiting for the result of the request, maintaining an efficient program flow.

2. Definition of Future

Future is an object provided by DART for asynchronous programming, representing a promise of the result of a specific operation. Future is generally used to indicate the result of asynchronous tasks such as network requests, file operations, or time delays. A Future can have two states:

  • Uncompleted: The operation has not yet been completed.
  • Completed: The result has been successfully returned or an error has occurred.

3. How to Use Future

Using Future is very simple. You can handle asynchronous tasks more intuitively using the async and await keywords.

3.1. Creating a Future

A Future can immediately return a value or throw an error using the Future.value() or Future.error() methods. Here is a simple example of creating a Future:


void main() {
    Future futureValue = Future.value("Hello, Future!");
    futureValue.then((value) {
        print(value); // Hello, Future!
    });
}

3.2. Using Future in Asynchronous Methods

Asynchronous methods return a Future, so you need to add the async keyword to the method. For example, an asynchronous method to fetch data from an API looks like this:


Future fetchData() async {
    await Future.delayed(Duration(seconds: 2));
    return "Data from server";
}

4. Checking the State of a Future

If you want to check the state of a Future, you can use the isCompleted, isCompleted, and isError properties. Here’s an actual usage example:


void main() async {
    Future futureValue = fetchData();
    
    futureValue.then((value) {
        print("Received: $value");
    }).catchError((error) {
        print("Error occurred: $error");
    });
    
    print("Future is completed: ${futureValue.isCompleted}");
}

5. Difference Between Future and Callback

In the past, callbacks were used for asynchronous programming. A callback is a function that is called after a specific operation is completed. However, this could lead to a problem known as callback hell, which often reduces readability. In contrast, using Future makes the code much cleaner and easier to read.

5.1. Callback Example


void fetchDataWithCallback(Function callback) {
    Future.delayed(Duration(seconds: 2), () {
        callback("Data from server");
    });
}

5.2. Future Example


void main() async {
    String data = await fetchData();
    print(data);
}

6. Applications of Future

There are various ways to use Future. Here, we will explain the utilization of Future through some practical examples.

6.1. Calling REST API

In many cases, a situation arises where data needs to be fetched asynchronously from a REST API. This can be implemented using Future. Here is an example of requesting data using the HTTP package:


import 'package:http/http.dart' as http;
import 'dart:convert';

Future fetchPost() async {
    final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));

    if (response.statusCode == 200) {
        final post = json.decode(response.body);
        print('Title: ${post['title']}');
    } else {
        throw Exception('Failed to load post');
    }
}

void main() {
    fetchPost();
}

6.2. Reading Files

In Flutter, Future is also used to read data from the file system. Here is an example of reading data from a local file:


import 'dart:io';

Future readFile(String path) async {
    final file = File(path);
    String contents = await file.readAsString();
    return contents;
}

void main() async {
    String data = await readFile('example.txt');
    print(data);
}

7. Future and Stream

While Future returns a single value result, Stream can return multiple values through asynchronous operations. Understanding the structure of Future and Stream allows you to select the appropriate tool for effective asynchronous programming.

7.1. Stream Example


Stream countStream() async* {
    for (int i = 1; i <= 5; i++) {
        await Future.delayed(Duration(seconds: 1));
        yield i;
    }
}

void main() async {
    await for (var count in countStream()) {
        print(count);
    }
}

8. Advantages and Disadvantages of Future

Future effectively supports asynchronous programming, but it is important to understand its advantages and disadvantages.

8.1. Advantages

  • Asynchronous tasks can be easily managed.
  • The code is more concise and has higher readability.
  • It provides catchError for error handling, helping to write stable code.

8.2. Disadvantages

  • It is important to manage the state properly during asynchronous tasks.
  • Complex asynchronous tasks can make code maintenance difficult.

9. Conclusion

Future is an important element for implementing asynchronous programming in Flutter. I hope this post has helped you understand the concept of Future, how to use it, and various examples. Try to utilize Future to create efficient and responsive applications through asynchronous programming!

10. References

Flutter Course: Completing the Lotto App UI

Hello! In this course, we will take a detailed look at how to complete the user interface (UI) of a lottery app using Flutter. This course will be beneficial for everyone, from those who are new to Flutter to those who have experience. Of course, if you have learned the basic concepts and components of Flutter through previous courses, you will find it easier to follow along.

Table of Contents

1. Overview

We will implement a feature that allows users to easily generate and verify numbers through the lottery app. The lottery app requires a simple UI and user-friendly design. This will enable users to generate and easily check their lottery numbers. In this course, we will complete the UI of the lottery app and add basic lottery number generation functionality.

2. Environment Setup

The process of installing and setting up Flutter is as follows:

  1. Download and install the Flutter SDK.
  2. You can use VS Code or Android Studio as your IDE. This course will focus on Android Studio.
  3. Set up the emulator in Android Studio.
  4. Create a Flutter project and install the necessary plugins.

3. Project Creation

Now, let’s create a new Flutter project. Open Android Studio and follow these steps:

  1. Select New > New Flutter Project from the File menu.
  2. Select Flutter Application and then click Next.
  3. Enter the Project Name, Project Location, etc., and click Finish.

The basic structure of the Flutter project has now been created. Please open the main.dart file.

4. UI Components

The UI of the lottery app consists of the following key components:

  • App Bar: Includes the title and menu of the app.
  • Number Selection Area: The area that displays the randomly generated lottery numbers.
  • Button: A button is needed to generate the numbers.

Now, let’s write the code to configure the UI. Below is the basic UI layout:


import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Lottery Number Generator',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: LottoHome(),
    );
  }
}

class LottoHome extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Lottery Number Generator'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'Generated Numbers:',
              style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
            // Number output section
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {},
              child: Text('Generate Numbers'),
            ),
          ],
        ),
      ),
    );
  }
}

The code above sets up the basic app structure and places the app bar and button.

5. Design Principles

The most important point in UI design is to consider the user experience. You should create an intuitive UI that users can easily understand and use. You must adhere to the following design principles:

  • Consistency: The colors, button styles, etc., used in all screens should be consistently maintained.
  • Accessibility: You need to consider accessibility so that all users can easily use the app.
  • Clarity: All elements of the app should be clearly displayed and should not confuse the user.

6. Required Packages

You can use several packages to generate lottery numbers. For example, you can use a package like random_number. To do this, add the following to the pubspec.yaml file:


dependencies:
  flutter:
    sdk: flutter
  random_number: ^1.0.0

This package allows you to generate random numbers. After adding the package, install it using the flutter pub get command.

7. Implementing the Lottery Number Generator

Now, we will implement the function to randomly generate lottery numbers. We will modify the code so that numbers are generated when the button is clicked:


import 'package:flutter/material.dart';
import 'dart:math';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Lottery Number Generator',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: LottoHome(),
    );
  }
}

class LottoHome extends StatefulWidget {
  @override
  _LottoHomeState createState() => _LottoHomeState();
}

class _LottoHomeState extends State {
  List _lottoNumbers = [];

  void _generateLottoNumbers() {
    final Random random = Random();
    _lottoNumbers = List.generate(6, (index) => random.nextInt(45) + 1)
      ..sort();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Lottery Number Generator'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'Generated Numbers: ${_lottoNumbers.join(', ')}',
              style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _generateLottoNumbers,
              child: Text('Generate Numbers'),
            ),
          ],
        ),
      ),
    );
  }
}

The code above generates random lottery numbers and displays them on the screen when the button is clicked.

8. Final Code

Now let’s integrate the code we have written so far to complete the final code:


import 'package:flutter/material.dart';
import 'dart:math';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Lottery Number Generator',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: LottoHome(),
    );
  }
}

class LottoHome extends StatefulWidget {
  @override
  _LottoHomeState createState() => _LottoHomeState();
}

class _LottoHomeState extends State {
  List _lottoNumbers = [];

  void _generateLottoNumbers() {
    final Random random = Random();
    _lottoNumbers = List.generate(6, (index) => random.nextInt(45) + 1)
      ..sort();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Lottery Number Generator'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'Generated Numbers: ${_lottoNumbers.join(', ')}',
              style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: _generateLottoNumbers,
              child: Text('Generate Numbers'),
            ),
          ],
        ),
      ),
    );
  }
}

9. Conclusion

In this course, we have step-by-step examined how to complete the user interface (UI) of a lottery app using Flutter. We configured the UI and implemented the functionality to generate lottery numbers when the button is clicked. Through this course, you have learned the basic methods of UI configuration in Flutter and state management.

Feel free to explore adding more complex features or ways to enhance the user experience. Flutter is a very powerful framework that provides the possibility to develop various apps. Thank you!

Flutter Course: Improving the Lotto App UI

Hello, developers! In this course, we will learn how to improve the user interface (UI) of the Lotto app. In the previous course, we implemented features to generate Lotto numbers and display the results. Now, let’s enhance the UI to improve the user experience further.

1. Project Setup and Existing Code Review

First, let’s take another look at the existing Lotto app project. The existing Lotto app has only basic features like a number generator. When the app is run, randomly generated Lotto numbers are displayed on the screen.

The structure of the existing code is as follows.


import 'package:flutter/material.dart';
import 'dart:math';

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

class LottoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Lotto App',
      home: LottoHome(),
    );
  }
}

class LottoHome extends StatefulWidget {
  @override
  _LottoHomeState createState() => _LottoHomeState();
}

class _LottoHomeState extends State {
  List lottoNumbers = [];

  void generateNumbers() {
    lottoNumbers = List.generate(6, (index) => Random().nextInt(45) + 1)..sort();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Lotto Number Generator')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'Generated Lotto Numbers:',
              style: TextStyle(fontSize: 24),
            ),
            SizedBox(height: 20),
            Text(
              lottoNumbers.join(', '),
              style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
            ),
            SizedBox(height: 30),
            ElevatedButton(
              onPressed: generateNumbers,
              child: Text('Generate Numbers'),
            ),
          ],
        ),
      ),
    );
  }
}

Now let’s explore various ways to improve the UI.

2. Establishing a UI Improvement Plan

To improve the UI, we can consider the following elements:

  • Changing the color scheme
  • Adjusting font styles and sizes
  • Applying card or grid layouts to make the numbers stand out more
  • Improving button design
  • Adding animation effects

2.1 Changing the Color Scheme

Using an eye-catching and enjoyable color combination instead of a simple color palette can provide a pleasant experience for users. For example, set a light color for the background and a contrasting dark color for the text.

2.2 Adjusting Font Styles and Sizes

To create a more attractive user experience, various font styles can be applied. Try using more unique fonts than the default one to change the mood of the app. You can easily apply a variety of fonts using Google Fonts.

2.3 Applying Card or Grid Layouts

By outputting Lotto numbers using cards, information can be conveyed more clearly. Additionally, let’s apply a grid layout to arrange the Lotto numbers.

2.4 Improving Button Design

Rather than using the default button, customize a button that includes selectable color combinations and text styles to emphasize button presses more.

2.5 Adding Animation Effects

Adding animation effects to buttons or UI elements can significantly enhance the user experience. Flutter provides various tools to easily implement animations.

3. Implementing UI Improvements

Now, based on the established plan, let’s modify the actual code to enhance the existing Lotto app by correcting certain elements.

3.1 Changing the Color Scheme

First, let’s change the color scheme to increase the contrast between the background and the text. Modify the build method in the main.dart file.


class LottoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Lotto App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        scaffoldBackgroundColor: Colors.lightBlue[50],
        textTheme: TextTheme(
          bodyText1: TextStyle(color: Colors.grey[800]),
          bodyText2: TextStyle(color: Colors.black),
        ),
      ),
      home: LottoHome(),
    );
  }
}

3.2 Adjusting Font Styles and Sizes

Next, let’s change the default text style. We can apply a more attractive font by adding the style property to the Text widget.


Text(
  'Generated Lotto Numbers:',
  style: TextStyle(
    fontSize: 28,
    fontWeight: FontWeight.bold,
    fontFamily: 'Roboto',
  ),
),

3.3 Applying Grid Layout

We will change the display of Lotto numbers to a grid format. Adding the code below will wrap each number in a card, making it visually appealing.


GridView.builder(
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 3,
    childAspectRatio: 1,
    crossAxisSpacing: 10,
    mainAxisSpacing: 10,
  ),
  itemCount: lottoNumbers.length,
  itemBuilder: (context, index) {
    return Card(
      color: Colors.yellowAccent,
      child: Center(
        child: Text(
          lottoNumbers[index].toString(),
          style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold),
        ),
      ),
    );
  },
),

3.4 Improving Button Design

To enhance the button design, we will add properties to the ElevatedButton to change its style.


ElevatedButton(
  onPressed: generateNumbers,
  style: ElevatedButton.styleFrom(
    primary: Colors.blue,
    onPrimary: Colors.white,
    padding: EdgeInsets.symmetric(horizontal: 25, vertical: 15),
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
  ),
  child: Text('Generate Numbers', style: TextStyle(fontSize: 20)),
),

3.5 Adding Animation Effects

We will use AnimatedContainer to apply animation effects when the button is clicked. The animation effect will be activated upon pressing the button.


AnimatedContainer(
  duration: Duration(milliseconds: 200),
  decoration: BoxDecoration(
    color: buttonPressed ? Colors.green : Colors.blue,
    borderRadius: BorderRadius.circular(30),
  ),
  child: ElevatedButton(
    onPressed: () {
      setState(() {
        buttonPressed = !buttonPressed;
      });
      generateNumbers();
    },
    child: Text('Generate Numbers', style: TextStyle(fontSize: 20)),
  ),
),

4. Final Code

We will integrate all of the above code to create the final code as follows.


import 'package:flutter/material.dart';
import 'dart:math';

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

class LottoApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Lotto App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        scaffoldBackgroundColor: Colors.lightBlue[50],
        textTheme: TextTheme(
          bodyText1: TextStyle(color: Colors.grey[800]),
          bodyText2: TextStyle(color: Colors.black),
        ),
      ),
      home: LottoHome(),
    );
  }
}

class LottoHome extends StatefulWidget {
  @override
  _LottoHomeState createState() => _LottoHomeState();
}

class _LottoHomeState extends State {
  List lottoNumbers = [];
  bool buttonPressed = false;

  void generateNumbers() {
    lottoNumbers = List.generate(6, (index) => Random().nextInt(45) + 1)..sort();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Lotto Number Generator')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Generated Lotto Numbers:', style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold, fontFamily: 'Roboto')),
            SizedBox(height: 20),
            Expanded(
              child: GridView.builder(
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 3,
                  childAspectRatio: 1,
                  crossAxisSpacing: 10,
                  mainAxisSpacing: 10,
                ),
                itemCount: lottoNumbers.length,
                itemBuilder: (context, index) {
                  return Card(
                    color: Colors.yellowAccent,
                    child: Center(
                      child: Text(
                        lottoNumbers[index].toString(),
                        style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold),
                      ),
                    ),
                  );
                },
              ),
            ),
            SizedBox(height: 30),
            ElevatedButton(
              onPressed: () {
                setState(() {
                  buttonPressed = !buttonPressed;
                });
                generateNumbers();
              },
              style: ElevatedButton.styleFrom(
                primary: Colors.blue,
                onPrimary: Colors.white,
                padding: EdgeInsets.symmetric(horizontal: 25, vertical: 15),
                shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
              ),
              child: Text('Generate Numbers', style: TextStyle(fontSize: 20)),
            ),
          ],
        ),
      ),
    );
  }
}

5. Conclusion and Future Improvement Directions

So far, we have learned how to improve the UI of the Lotto app. By improving the app’s design in various ways, we can enhance the user’s experience. In the future, we can enrich the Lotto app even further by adding additional features, such as visualizing historical data for Lotto numbers or providing a record-keeping function for the numbers generated by the user.

I hope this course has been helpful to developers like you, and I look forward to bringing you another topic in the next course. Thank you for reading to the end!