Flutter Course: Implementing Features of Lotto App 13.3

Flutter is Google’s UI toolkit for building natively compiled applications for mobile from a single codebase. It performs well on various platforms and provides an attractive user interface. In this course, we will learn how to implement the functionality of a lottery app using Flutter. The lottery app is a useful mobile application that allows users to draw lottery numbers and compare them with winning numbers to check the results.

1. Preparing the Project

To create a lottery app, we first need to set up the Flutter environment. Install the Flutter SDK and create a new project.

flutter create lotto_app

Navigate to the created project directory and install the necessary packages.

cd lotto_app

2. Designing the App Structure

The lottery app has two main functions: allowing users to select numbers and checking the winning numbers. When designing the app’s structure, it is important to clearly separate each screen with a focus on user experience.

2.1 Screen Composition

  • Main Screen: A screen with buttons for number selection and drawing
  • Result Screen: A screen that shows the results by comparing with the winning numbers

To implement these two screens, we will utilize Flutter’s Navigator to manage navigation between screens.

3. Implementing Number Selection Functionality

3.1 UI Composition

We will set up the UI on the main screen to allow users to select lottery numbers. We will use GridView to arrange the numbers and allow user selection.

import 'package:flutter/material.dart';

class MainScreen extends StatefulWidget {
  @override
  _MainScreenState createState() => _MainScreenState();
}

class _MainScreenState extends State {
  List selectedNumbers = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Lottery Number Picker')),
      body: GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
        itemCount: 45,
        itemBuilder: (context, index) {
          return GestureDetector(
            onTap: () => _onNumberTap(index + 1),
            child: Container(
              margin: EdgeInsets.all(4),
              decoration: BoxDecoration(
                color: selectedNumbers.contains(index + 1) ? Colors.blue : Colors.grey[200],
                borderRadius: BorderRadius.circular(10),
              ),
              child: Center(child: Text('${index + 1}', style: TextStyle(fontSize: 24))),
            ),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _drawNumbers,
        tooltip: 'Draw Numbers',
        child: Icon(Icons.monetization_on),
      ),
    );
  }

  void _onNumberTap(int number) {
    setState(() {
      if (selectedNumbers.contains(number)) {
        selectedNumbers.remove(number);
      } else {
        if (selectedNumbers.length < 6) {
          selectedNumbers.add(number);
        }
      }
    });
  }

  void _drawNumbers() {
    // Implement drawing logic
  }
}

3.2 Number Drawing Logic

When the user selects 6 numbers, we generate the winning numbers randomly, ensuring there are no duplicates.


void _drawNumbers() {
  Random random = Random();
  Set winningNumbers = {};
  
  while (winningNumbers.length < 6) {
    winningNumbers.add(random.nextInt(45) + 1);
  }

  Navigator.push(
    context,
    MaterialPageRoute(
      builder: (context) => ResultScreen(selectedNumbers: selectedNumbers.toSet(), winningNumbers: winningNumbers.toSet()),
    ),
  );
}

4. Implementing the Result Screen

We create a screen that shows the results by comparing the winning numbers with the numbers selected by the user.

class ResultScreen extends StatelessWidget {
  final Set selectedNumbers;
  final Set winningNumbers;

  ResultScreen({required this.selectedNumbers, required this.winningNumbers});

  @override
  Widget build(BuildContext context) {
    int matches = selectedNumbers.intersection(winningNumbers).length;

    return Scaffold(
      appBar: AppBar(title: Text('Result Screen')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('Winning Numbers: ${winningNumbers.join(", ")}', style: TextStyle(fontSize: 24)),
            Text('Your Selected Numbers: ${selectedNumbers.join(", ")}', style: TextStyle(fontSize: 24)),
            Text('Matched Count: $matches', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
          ],
        ),
      ),
    );
  }
}

5. Final Structure of the App

We will combine all the classes implemented above to create the final structure of the lottery app. Navigation between screens occurs through Navigator. When the user selects numbers and clicks the draw button, they will be taken to the result screen.

6. Improving the App Design

With the basic functionalities implemented, we will enhance the app’s design to provide a more attractive user experience. For example, we can unify the color scheme and add animation effects to the buttons.

7. Additional Feature Suggestions

  • Winning Probability Calculator: Calculates the winning probability based on the user’s selected numbers.
  • View Previous Draw Results: Stores previous winning numbers and displays them to the user.
  • Lottery Statistics: Visually presents various data about monthly statistics.

8. Conclusion

In this course, we learned how to implement basic functionalities for a lottery app using Flutter. The lottery app is a simple yet useful application that allows users to experience fun through number selection, drawing, and checking results. Additional features can provide a richer experience and evolve into your brand.

When undertaking any project, it is important to start from the basics and gradually add more complex features. Through this course, I hope you learn the basic functionalities of Flutter and challenge yourself to work on more complex app developments.

References

Flutter Course: 13.2 Collection Types

Hello! In this article, we will take a deep dive into a key component of Flutter, which is the collection types. Flutter is not only a powerful UI toolkit but is also based on the Dart language. In Dart, collection types play a very important role in managing data structures. They help efficiently process and manage various types of data.

Basic Concepts of Collection Types

Collection types are fundamentally ways to group and process data. Dart primarily offers three collection types: List, Set, and Map.

  • List: A set of ordered data. It allows duplicate values and accesses data using an index.
  • Set: A set of data that does not allow duplicates. It is very useful for adding, removing, and searching.
  • Map: A set of data composed of key-value pairs. Each key is unique, and values are accessed through these keys.

1. List

A List is a collection type that allows storing multiple data while maintaining the order of data. A List can be declared as follows:

List numbers = [1, 2, 3, 4, 5];

In the example above, we created a List of type int. Lists provide various methods to handle data. For example:

  • add(item): Adds a new item at the end of the list.
  • remove(item): Removes a specific item from the list.
  • contains(item): Checks if a specific item is included in the list.

To gain a deeper understanding of Lists, let’s look at the example below:


void main() {
    List fruits = ["apple", "banana", "cherry"];
    
    // Add item
    fruits.add("orange");
    
    // Remove item
    fruits.remove("banana");

    // Print list contents
    print(fruits); // Output: [apple, cherry, orange]
    
    // Check if specific item is included
    if (fruits.contains("cherry")) {
        print("Cherry is in the list.");
    }
}

1.1 Iterating through a List

To access each item in a List, you can use a loop:


for (var fruit in fruits) {
    print(fruit);
}

Alternatively, you can access items directly using an index:


for (int i = 0; i < fruits.length; i++) {
    print(fruits[i]);
}

2. Set

A Set is a collection that does not allow duplicates. If you try to store duplicate values, they will be ignored. A Set can be declared as follows:

Set colors = {"red", "blue", "green"};

You can also use various methods in a Set:

  • add(item): Adds an item to the Set. If it is a duplicate, it will be ignored.
  • remove(item): Removes a specific item from the Set.
  • contains(item): Checks if a specific item is included in the Set.

Here is an example of using a Set:


void main() {
    Set animals = {"cat", "dog", "bird"};
    
    // Add item
    animals.add("rabbit");
    
    // Add duplicate item (ignored)
    animals.add("cat");

    // Print Set contents
    print(animals); // Output: {cat, dog, bird, rabbit}
    
    // Check if specific item is included
    if (animals.contains("dog")) {
        print("The dog is in the Set.");
    }
}

3. Map

A Map is a collection that stores data in key-value pairs. The key acts as a unique identifier for each value. A Map can be declared as follows:

Map studentGrades = {"John": 85, "Kim": 90};

A Map also provides several methods to manage data:

  • put(key, value): Adds a new key-value pair to the Map.
  • remove(key): Removes the item associated with a specific key.
  • containsKey(key): Checks if a specific key is included in the Map.

3.1 Example of Using a Map

Here is a simple example of using a Map:


void main() {
    Map studentGrades = {"John": 85, "Kim": 90};
    
    // Add new student
    studentGrades["Lee"] = 95;
    
    // Print specific student's grades
    print("Lee's grades: ${studentGrades["Lee"]}");
    
    // Print all students and their grades
    studentGrades.forEach((name, grade) {
        print("$name: $grade");
    });
}

Utilizing Collection Types

Let’s look at a practical example of utilizing collection types. This will help us understand how to use data structures in real development situations.

Example: Storing User Information

Assuming we are developing an application to store user information. In this case, we can utilize Lists and Maps:


class User {
    String name;
    int age;

    User(this.name, this.age);
}

void main() {
    List users = [];
    
    // Add users
    users.add(User("John", 25));
    users.add(User("Kim", 30));

    // Print user information
    for (var user in users) {
        print("Name: ${user.name}, Age: ${user.age}");
    }
    
    // Create Map with user names as keys
    Map userMap = {for (var user in users) user.name: user};

    // Search for a user
    String searchName = "John";
    if (userMap.containsKey(searchName)) {
        User foundUser = userMap[searchName]!;
        print("${foundUser.name}'s age is ${foundUser.age}.");
    }
}

6. Conclusion

In this session, we took a close look at the collection types in Flutter, which can effectively manage data structures: List, Set, and Map. Each collection type has its distinct characteristics, and developers can choose the appropriate data depending on the situation. Through effective data management, developers can write more efficient and maintainable code.

Additionally, one should also consider potential performance issues or memory management that may arise when using collection types. Keep these points in mind as you design your own appropriate data structures. We will strive to cover in-depth topics in the next session as well. Thank you!

Flutter Course: 13.1 Implementing Conditional Statements and Login Functionality

Hello! In this tutorial, we will learn how to implement a login feature using conditional statements in Flutter. Flutter is a UI toolkit developed by Google that helps create applications easily across various platforms, including iOS and Android. Conditional statements are important elements that control the flow of programs. Let’s get started.

1. Overview of Conditional Statements

Conditional statements are used to evaluate whether a given condition is true or false to determine the flow of a program. In Flutter (or Dart language), we primarily use ‘if’, ‘else if’, ‘else’, and ‘switch’ statements to evaluate conditions.

        
        // Simple Example
        if (condition) {
            // Code executed when the condition is true
        } else {
            // Code executed when the condition is false
        }
        
    

By utilizing such conditional statements, we can provide a dynamic user experience based on user input. This allows us to implement the essential login screen feature in Flutter applications.

2. Designing the Login Screen

The login screen consists of a simple form that takes the user’s ID and password. In this tutorial, we will implement the login screen using text fields and a button.

2.1 Template Structure

        
        import 'package:flutter/material.dart';

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

        class MyApp extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
            return MaterialApp(
              home: LoginScreen(),
            );
          }
        }

        class LoginScreen extends StatefulWidget {
          @override
          _LoginScreenState createState() => _LoginScreenState();
        }

        class _LoginScreenState extends State {
          // Variables to store the ID and password
          String _username = '';
          String _password = '';

          @override
          Widget build(BuildContext context) {
            return Scaffold(
              appBar: AppBar(
                title: Text('Login Screen'),
              ),
              body: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  children: [
                    TextField(
                      decoration: InputDecoration(labelText: 'Username'),
                      onChanged: (value) {
                        _username = value;
                      },
                    ),
                    TextField(
                      decoration: InputDecoration(labelText: 'Password'),
                      obscureText: true,
                      onChanged: (value) {
                        _password = value;
                      },
                    ),
                    ElevatedButton(
                      onPressed: _login,
                      child: Text('Login'),
                    ),
                  ],
                ),
              ),
            );
          }
        }
        
    

3. Implementing the Login Functionality

When the login button is clicked, we need to implement functionality to check whether the login is successful based on certain conditions. At this point, we will set a predefined ID and password for verification.

        
        void _login() {
          // Predefined username and password
          const String predefinedUsername = 'user';
          const String predefinedPassword = 'password';

          // Use conditional statements to check login
          if (_username == predefinedUsername && _password == predefinedPassword) {
            // Login successful
            showDialog(
              context: context,
              builder: (BuildContext context) {
                return AlertDialog(
                  title: Text('Login Success'),
                  content: Text('Welcome!'),
                  actions: [
                    TextButton(
                      child: Text('OK'),
                      onPressed: () {
                        Navigator.of(context).pop();
                      },
                    ),
                  ],
                );
              },
            );
          } else {
            // Login failed
            showDialog(
              context: context,
              builder: (BuildContext context) {
                return AlertDialog(
                  title: Text('Login Failed'),
                  content: Text('Incorrect username or password.'),
                  actions: [
                    TextButton(
                      child: Text('OK'),
                      onPressed: () {
                        Navigator.of(context).pop();
                      },
                    ),
                  ],
                );
              },
            );
          }
        }
        
    

The above code checks whether the username and password entered by the user match the predefined information. If they match, a login success message is displayed in a popup; if they do not match, a login failure message is shown. We are controlling the flow using conditional statements.

4. Screen Transition on Login Success

Upon successful login, you can transition the user to another screen. For example, you can move to the main screen. Below is the code for transitioning the screen upon successful login.

        
        void _login() {
          const String predefinedUsername = 'user';
          const String predefinedPassword = 'password';

          if (_username == predefinedUsername && _password == predefinedPassword) {
            // On login success, navigate to the main screen
            Navigator.pushReplacement(
              context,
              MaterialPageRoute(builder: (context) => MainScreen()),
            );
          } else { /* ... (Handle login failure) ... */ }
        }

        class MainScreen extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
            return Scaffold(
              appBar: AppBar(
                title: Text('Main Screen'),
              ),
              body: Center(
                child: Text('Welcome!'),
              ),
            );
          }
        }
        
    

In the above content, using `Navigator.pushReplacement` allows you to transition to a new screen upon login success and removes the previous screen from the stack, preventing you from returning to it.

5. Utilizing Conditional Statements Again

Conditional statements can be used to add various features based on user input, beyond just the login functionality. For example, consider providing a password recovery link when the user forgets their password.

        
        // Example of adding a password recovery button
        Column(
          children: [
            // Existing input fields...
            TextButton(
              onPressed: () {
                // Password recovery handling
                _forgotPassword();
              },
              child: Text('Forgot Password?'),
            ),
          ],
        );

        void _forgotPassword() {
          // Handle password recovery logic
          showDialog(
            context: context,
            builder: (BuildContext context) {
              return AlertDialog(
                title: Text('Password Recovery'),
                content: Text('A reset link has been sent to the registered email.'),
                actions: [
                  TextButton(
                    child: Text('OK'),
                    onPressed: () {
                      Navigator.of(context).pop();
                    },
                  ),
                ],
              );
            },
          );
        }
        
    

6. Conclusion

In this tutorial, we learned how to implement a login functionality using Flutter and how to determine the success of the login using conditional statements. Properly utilizing conditional statements can significantly improve user experience and help in creating dynamic applications.

Now, you can implement your own login feature and add more functionalities. Through various exercises and practices, you can understand and implement complex logic using various conditional statements.

Thank you!

Flutter Course: 12.5 SingleChildScrollView Widget

Flutter is one of the most popular frameworks for developing mobile applications. With a variety of widgets, developers can easily design complex UIs. In this article, we will delve deeply into the SingleChildScrollView widget in Flutter and detail its usage with real-world examples.

Simple Example

The SingleChildScrollView widget provides a scrollable view when the child widget exceeds the size of the screen. The most basic usage is as follows:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('SingleChildScrollView Example'),
        ),
        body: SingleChildScrollView(
          child: Column(
            children: [
              Container(height: 200, color: Colors.red),
              Container(height: 200, color: Colors.green),
              Container(height: 200, color: Colors.blue),
              Container(height: 200, color: Colors.yellow),
              Container(height: 200, color: Colors.purple),
            ],
          ),
        ),
      ),
    );
  }
}

In the above example, multiple Container widgets are arranged vertically. If the height exceeds the screen, the SingleChildScrollView is activated, allowing the user to scroll to see all the content.

SingleChildScrollView Properties

SingleChildScrollView offers various properties. Each property is used to customize the scrollable view. The main properties are as follows:

  • padding: You can set the external padding for the scroll view.
  • scrollDirection: Sets the direction of the scroll. The default is vertical.
  • reverse: Reverses the scroll direction.
  • controller: Sets a ScrollController for controlling the scroll.
  • physics: Sets the physical properties of the scroll behavior.

Example: Padding and ScrollDirection

SingleChildScrollView(
  padding: EdgeInsets.all(16.0),
  scrollDirection: Axis.horizontal,
  child: Row(
    children: [
      Container(width: 200, height: 100, color: Colors.red),
      Container(width: 200, height: 100, color: Colors.green),
      Container(width: 200, height: 100, color: Colors.blue),
      Container(width: 200, height: 100, color: Colors.yellow),
    ],
  ),
)

The above example sets the padding for SingleChildScrollView and changes the scroll direction to horizontal. This allows the user to create a UI that can be scrolled horizontally.

Using ScrollController

Using ScrollController, you can control the scroll position or perform actions such as scrolling to a specific position. Below is an example of using ScrollController:

class MyHomePage extends StatelessWidget {
  final ScrollController _scrollController = ScrollController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('ScrollController Example'),
      },
      body: SingleChildScrollView(
        controller: _scrollController,
        child: Column(
          children: [
            Container(height: 600, color: Colors.red),
            Container(height: 600, color: Colors.green),
            ElevatedButton(
              onPressed: () {
                _scrollController.animateTo(
                  100.0,
                  duration: Duration(seconds: 1),
                  curve: Curves.easeInOut,
                );
              },
              child: Text('Scroll to 100'),
            ),
          ],
        ),
      ),
    );
  }
}

The above example demonstrates the functionality of automatically changing the scroll when the ElevatedButton is pressed. When the button is clicked, the scroll moves down by 100 pixels.

Setting Scroll Physical Responses

The physics property of SingleChildScrollView controls the physical response of the scroll. There are various physical responses, including the following:

  • AlwaysScrollableScrollPhysics: Ensures the view is always scrollable.
  • BouncingScrollPhysics: Adds a bounce effect when the scroll reaches the end.
  • ClampingScrollPhysics: Prevents further scrolling when the scroll reaches the end.

Example: Using BouncingScrollPhysics

SingleChildScrollView(
  physics: BouncingScrollPhysics(),
  child: Column(
    children: [
      Container(height: 600, color: Colors.red),
      Container(height: 600, color: Colors.green),
    ],
  ),
)

Using with State Management

SingleChildScrollView is useful for displaying dynamic content when used with state management patterns. For example, you can manage data state using Provider or Riverpod. Below is a simple example of using it with Provider.

class NumberProvider extends ChangeNotifier {
  List numbers = [];

  void addNumber() {
    numbers.add(numbers.length);
    notifyListeners();
  }
}

class NumberList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer(
      builder: (context, provider, child) {
        return SingleChildScrollView(
          child: Column(
            children: provider.numbers.map((number) {
              return ListTile(title: Text('Number: $number'));
            }).toList(),
          ),
        );
      },
    );
  }
}

This example demonstrates the ability to dynamically update the list by adding numbers through a button.

Conclusion

The SingleChildScrollView widget is a very useful tool in Flutter for displaying long content that requires scrolling. You can effectively utilize SingleChildScrollView through various properties and examples. This enables you to develop user-friendly apps that implement various scrolling-related features.

In this tutorial, we explored the basic usage and advanced features of the SingleChildScrollView widget. If you have any additional questions or need assistance, please leave a comment and I will be happy to respond. Thank you!

Flutter Course: 12.4 MediaQuery.of(context)

Flutter provides various widgets to deliver a consistent user interface across different platforms. Among them, MediaQuery is used to obtain various information such as the screen size, orientation, and resolution of the device. This tutorial will explain in detail how to use MediaQuery.of(context).

1. What is MediaQuery?

MediaQuery is one of the built-in widgets in Flutter that helps adjust the app’s UI to fit the device’s screen size and properties. It allows the UI to be optimized by determining the environment of the device.

2. Basic Usage of MediaQuery.of(context)

Using MediaQuery.of(context), you can get the MediaQueryData for the current BuildContext. For example, here’s how to obtain the screen width and height:


    var mediaQuery = MediaQuery.of(context);
    var screenWidth = mediaQuery.size.width;
    var screenHeight = mediaQuery.size.height;
    

3. Creating Layouts Using MediaQuery

You can utilize media queries to create appropriate layouts for various screen sizes. For example, you can display different widgets based on the screen size.


    @override
    Widget build(BuildContext context) {
        var screenWidth = MediaQuery.of(context).size.width;
        return Scaffold(
            appBar: AppBar(title: Text('MediaQuery Example')),
            body: Center(
                child: screenWidth < 600
                    ? Text('Small Screen')
                    : Text('Big Screen'),
            ),
        );
    }
    

4. Utilizing MediaQuery for Dynamic UI Design

In Flutter, you can dynamically design the UI to fit various screen sizes. The following example shows how to apply different Padding based on screen size.


    @override
    Widget build(BuildContext context) {
        var padding = MediaQuery.of(context).size.width < 600 ? EdgeInsets.all(20) : EdgeInsets.all(50);
        return Padding(
            padding: padding,
            child: Text('Dynamic Padding Example'),
        );
    }
    

5. Explanation of MediaQueryData Properties

MediaQueryData provides various properties. Here are the explanations:

  • size: The size of the device's screen (Width, Height)
  • orientation: The orientation of the device (Portrait, Landscape)
  • devicePixelRatio: The pixel density of the screen
  • padding: The padding of the screen (Safe Area)
  • viewInsets: The portion of the screen affected by UI elements such as the software keyboard

6. Use Cases for MediaQuery

Here are some examples of using MediaQuery in real apps. Key cases include responsive design, support for various devices, and dynamic layout adjustments.

6.1 Example of Responsive Design

An example of an app utilizing responsive design is adjusting the number of items in a list view based on the screen width. Here, we will explain how to dynamically change the number of columns.

6.2 Support for Various Devices

By utilizing MediaQuery, you can easily design apps that support various resolutions and aspect ratios. For example, you can create UI elements tailored to the characteristics of each device.

7. Conclusion

MediaQuery is a crucial element for constructing layouts tailored to the device's environment in Flutter. This tutorial covered the basic usage of MediaQuery.of(context) and its use cases. Feel free to experiment with more features and application cases.