Flutter Course 11.6: Container Widget and BoxFit Property

Flutter is a UI toolkit that helps you showcase fast and beautiful applications to the world. In this chapter, we will take a closer look at the Container widget and one of its properties, BoxFit.

1. Introduction to the Container Widget

The Container widget is one of the most basic and essential widgets in Flutter, used to construct and position various UI elements. The Container widget has several properties and mainly performs the following functions:

  • Padding: Adjusts the internal spacing to set the distance between the child widget and the container boundary.
  • Margin: Adjusts the external spacing to set the distance between the container and other widgets.
  • Decoration: Sets design properties such as background, border, and shadow for the container.
  • Constraints: Used to constrain the size of the child widget.
  • Child: The widget to be placed inside the Container widget.

2. Introduction to the BoxFit Property

The BoxFit property determines how content like images will fit into the Container. This property is primarily used with the Image widget, allowing you to set how the image will be sized to fit the Container. The BoxFit property has several types, each with different behaviors:

2.1 BoxFit.contain

The BoxFit.contain property scales the child widget while maintaining its aspect ratio to fit within the size of the Container. This property ensures that the child widget does not exceed the boundaries of the Container. If the aspect ratio of the child widget differs from that of the Container, there may be margins at the top or bottom, or left or right.

2.2 BoxFit.cover

The BoxFit.cover property makes the child widget completely cover the Container. In this case, the child widget may exceed the boundaries of the Container, and some parts may be clipped to maintain the aspect ratio. This property is useful for creating certain design elements like background images.

2.3 BoxFit.fill

The BoxFit.fill property places the child widget by distorting it to fit the size of the Container. The aspect ratio of the child widget can change, and sometimes the original appearance of the child widget may be lost.

2.4 BoxFit.scaleDown

The BoxFit.scaleDown property reduces the size of the child widget to fit into the Container but ensures that it does not become smaller than its original size. This property only works when the child widget’s size is larger than the Container, while it maintains the original size when smaller.

2.5 BoxFit.none

The BoxFit.none property ensures that the child widget is not affected by the size of the Container at all. In this case, the child widget retains its original size, and the user may use other properties to adjust its position.

3. Example of Using Container and BoxFit

Now, let’s look at a practical code example to see how the Container and BoxFit properties work. The code below is an example that displays images with various BoxFit properties.

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('Container and BoxFit Example')),
        body: Column(
          children: [
            Container(
              width: 200,
              height: 200,
              decoration: BoxDecoration(color: Colors.blue),
              child: Image.asset('assets/example.jpg', fit: BoxFit.contain),
            ),
            Container(
              width: 200,
              height: 200,
              decoration: BoxDecoration(color: Colors.green),
              child: Image.asset('assets/example.jpg', fit: BoxFit.cover),
            ),
            Container(
              width: 200,
              height: 200,
              decoration: BoxDecoration(color: Colors.red),
              child: Image.asset('assets/example.jpg', fit: BoxFit.fill),
            ),
            Container(
              width: 200,
              height: 200,
              decoration: BoxDecoration(color: Colors.orange),
              child: Image.asset('assets/example.jpg', fit: BoxFit.scaleDown),
            ),
            Container(
              width: 200,
              height: 200,
              decoration: BoxDecoration(color: Colors.purple),
              child: Image.asset('assets/example.jpg', fit: BoxFit.none),
            ),
          ],
        ),
      ),
    );
  }
}

In the example above, we are displaying images using each of the BoxFit properties. As a result, we can compare how the images appear in each container.

4. Considerations When Using BoxFit

There are several points to be aware of when using the BoxFit property:

  • If the image is too large, using the BoxFit.cover setting may result in important parts being clipped, so consider which part the user wants to crop.
  • When using BoxFit.fill, the original aspect ratio may be distorted, making it unsuitable for images that need to maintain their aspect ratio.
  • Performance issues: Using high-resolution images can consume a lot of memory and affect page loading speed. In such cases, images should be optimized for use.

5. Conclusion

In this chapter, we explored the Container widget and BoxFit property in depth in Flutter. The Container widget serves as a fundamental building block of Flutter UI, allowing flexible layouts through various properties. The BoxFit property provides options on how to position content like images within the Container.

By understanding and utilizing the concepts of Container and BoxFit while developing apps with Flutter, you can create a more elegant and responsive UI. In the next lesson, we will explore another widget and its properties.

Final Note

Your journey in Flutter development continues. Having learned the Container and BoxFit properties, it’s time to build beautiful user interfaces for your applications. For more information, please refer to various Flutter-related documents or courses. Thank you!

Flutter Course, 11.5 Code Refactoring

In program development, code refactoring is a very important concept that refers to the process of improving the structure of the code while maintaining its functionality. This is also true in Flutter, where this process contributes to enhancing code readability, facilitating maintenance, and ultimately improving the overall quality of the project. In this course, we will closely examine how to perform code refactoring in Flutter, its benefits, and various techniques of code refactoring.

1. What is Code Refactoring?

Code refactoring refers to the process of changing already written code to improve its structure and readability without modifying the functionality of the code. This process is important for several reasons:

  • Improved code readability: When code is well-structured and concise, it becomes easier for other developers or oneself to understand the code later.
  • Ease of maintenance: Well-organized code reduces errors during modifications or additions, speeding up development.
  • Facilitated testing: The modularization of code simplifies unit testing, allowing problems to be identified early.

2. Code Refactoring in Flutter

Flutter is known for its concise syntax and various widgets for UI creation, but as business logic and UI structure become complex, the code can become complicated. This is when code refactoring is necessary. Let’s explore how to effectively perform refactoring in Flutter.

2.1. Separating Code

To enhance code readability, it is advisable to separate business logic from UI code. Flutter offers various design patterns for this purpose. The most commonly used patterns are MVC (Model-View-Controller) and MVVM (Model-View-ViewModel).

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Refactoring Example"),
      ),
      body: MyCustomWidget(),
    );
  }
}

class MyCustomWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text("Hello, Flutter!"),
    );
  }
}

2.2. Reusing Widgets

In Flutter, it is possible to reuse widgets. Instead of repeatedly using similar code, create widgets as classes and reuse them whenever needed. This reduces code duplication and makes maintenance easier since you only have to modify one location when changes are necessary.

class CustomButton extends StatelessWidget {
  final String label;
  final VoidCallback onPressed;

  CustomButton({required this.label, required this.onPressed});

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: onPressed,
      child: Text(label),
    );
  }
}

3. Techniques for Code Refactoring

Refactoring the code of a Flutter application can be accomplished through various techniques. Below are some representative techniques.

3.1. Eliminating Duplicate Code

If duplicated code exists in multiple locations, it is advisable to separate the common code into a single function to eliminate the duplicates. Removing duplicate code is the first step in code maintenance.

3.2. Separating Functions and Methods

When functions or methods become too lengthy, it becomes difficult to perform the given task. Such long functions or methods should be divided into smaller units to enhance readability and clarify the functionality of each part.

void longFunction() {
  // Separate the existing long function into smaller, clearer functions
  firstTask();
  secondTask();
  thirdTask();
}

void firstTask() {
  // First task
}

void secondTask() {
  // Second task
}

void thirdTask() {
  // Third task
}

3.3. Applying Object-Oriented Principles

Adhering to the principles of object-oriented programming greatly helps in improving code reusability and structure. Refer to the SOLID principles when defining classes and clearly delineate the responsibilities of each class.

4. Considerations Before Refactoring

Before proceeding with code refactoring, there are several considerations to keep in mind.

  • Functionality testing: Write functionality tests to ensure that the features remain the same before and after refactoring.
  • Using version control: When performing refactoring, commit code changes to a version control system to easily revert to a previous state.
  • Collaboration with the team: In a team development environment, it’s essential to communicate with team members before refactoring to inform them of the changes.

5. Various Code Refactoring Tools

Flutter developers can use various tools to assist with code refactoring. These include code analysis tools and the automatic refactoring features of IDEs.

5.1. Flutter Analyzer

Flutter provides Flutter Analyzer by default, which analyzes code quality and offers suggestions needed for refactoring. Users can easily carry out code improvement tasks through these suggestions.

5.2. IDE’s Automatic Refactoring Features

IDEs such as Visual Studio Code and Android Studio offer automatic refactoring features, eliminating the need for manual refactoring tasks. For example, changing variable names all at once, extracting methods, or altering class structures becomes easy.

6. Real-World Example: Refactoring Code in Flutter

Now, let’s look at the process of refactoring actual code. First, let’s write the code for a simple Flutter app.

class SimpleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Code Refactoring Example"),
        ),
        body: Column(
          children: [
            Text("Welcome to Flutter"),
            ElevatedButton(
              onPressed: () {
                // Button click logic
              },
              child: Text("Click Me"),
            )
          ],
        ),
      ),
    );
  }
}

The code above is a simple example. We will refactor the code to separate business logic and modify it for reusable widgets to improve readability.

class RefactoredApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Refactored Code"),
        ),
        body: HomePage(),
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        WelcomeText(),
        CustomButton(
          label: "Click Me",
          onPressed: () {
            // Logic to handle when the button is clicked
          },
        ),
      ],
    );
  }
}

class WelcomeText extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Text("Welcome to Flutter");
  }
}

The refactored code above clearly separates each code block, and the responsibilities of each class are well defined. This greatly enhances the readability and maintainability of the code.

7. Conclusion

Code refactoring is a very important task in program development. This is especially crucial in intuitive UI frameworks like Flutter. In this course, we have learned about the importance and techniques of refactoring, as well as practical applications. Regularly conducting refactoring is necessary to continuously improve code quality.

8. Additional Resources

If you want to learn more about refactoring techniques or examples related to Flutter, please refer to the following resources:

Flutter Course: 11.4 TextField Widget

Flutter is an open-source UI software development kit (SDK) developed by Google, used for creating mobile, web, and desktop applications. In this course, we will look at the TextField widget, one of Flutter’s UI components. The TextField is a basic widget that allows users to input text. Let’s explore the basic usage of the TextField widget, its various features, and how to customize it in detail.

1. Basic Structure of TextField

The TextField widget is a basic input field used to receive user input. To use it, you can create a widget with the following basic structure:

TextField(
  decoration: InputDecoration(
    border: OutlineInputBorder(), // Border style for the input field
    labelText: 'Text to enter',   // Label text
    hintText: 'Enter here',   // Hint text
  ),
  onChanged: (text) {
    // Callback that is called when the text changes
    print('Entered text: $text');
  },
)

Here, the decoration property defines the appearance of the input field. The labelText defines the label for the input field, and the hintText serves as a placeholder in the input field. The onChanged property is a callback function that is called every time the user enters text.

2. Key Properties of the TextField Widget

The TextField widget has several important properties that allow for finer control over the widget’s behavior and appearance. The following are the most commonly used properties:

  • controller: Uses an instance of TextEditingController to keep track of the current state of the input field.
  • obscureText: Set to true to hide text for secure input like passwords.
  • keyboardType: Set the type of keyboard that pops up for an improved user experience.
  • maxLines: Set the maximum number of lines inputted.
  • onSubmitted: A callback that is called when the user completes and submits their input.

Here is how to use these properties:

TextField(
  controller: myController,
  obscureText: true,
  keyboardType: TextInputType.emailAddress, 
  maxLines: 1, 
  onSubmitted: (value) {
    print('User entered value: $value');
  },
)

3. Using TextEditingController

You can use the TextEditingController to manage data and state related to the input field. This allows you to get or set the value in the input field. Here is an example of using the TextEditingController:

class MyTextFieldWidget extends StatefulWidget {
  @override
  _MyTextFieldWidgetState createState() => _MyTextFieldWidgetState();
}

class _MyTextFieldWidgetState extends State {
  TextEditingController myController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(
          controller: myController,
          decoration: InputDecoration(
            border: OutlineInputBorder(),
            labelText: 'Enter your email',
          ),
        ),
        ElevatedButton(
          onPressed: () {
            print('Entered email: ${myController.text}');
          },
          child: Text('Submit'),
        ),
      ],
    );
  }

  @override
  void dispose() {
    // Release the controller on widget removal to prevent memory leaks.
    myController.dispose();
    super.dispose();
  }
}

4. Different Types of TextField

Now let’s learn about the various types of TextField. In addition to the basic input field, there are multiple types of input fields you can customize.

4.1. Password Input Field

To create a password input field, set the obscureText property to true. Here is an example of a password input field:

TextField(
  obscureText: true,
  decoration: InputDecoration(
    border: OutlineInputBorder(),
    labelText: 'Enter your password',
  ),
)

4.2. Email Address Input Field

When accepting email input, set the keyboardType to TextInputType.emailAddress to display a dedicated keyboard for emails:

TextField(
  keyboardType: TextInputType.emailAddress,
  decoration: InputDecoration(
    border: OutlineInputBorder(),
    labelText: 'Enter your email',
  ),
)

4.3. Multi-line Input Field

To support the input of multiple lines of text, set the maxLines property:

TextField(
  maxLines: 5,
  decoration: InputDecoration(
    border: OutlineInputBorder(),
    labelText: 'Enter content here',
  ),
)

5. Styling the TextField

You can style the input field to make it more beautiful and intuitive using various properties. Let’s use the properties of InputDecoration?

  • fillColor: Sets the background color of the input field.
  • focusedBorder: Sets the border style when the input field is focused.
  • enabledBorder: Sets the border style when the input field is active.
  • errorText: Sets the text to display in case of input errors.

An example is as follows:

TextField(
  decoration: InputDecoration(
    fillColor: Colors.lightBlueAccent,
    filled: true,
    focusedBorder: OutlineInputBorder(
      borderSide: BorderSide(color: Colors.green, width: 2.0),
    ),
    enabledBorder: OutlineInputBorder(
      borderSide: BorderSide(color: Colors.blue, width: 2.0),
    ),
    errorText: 'Invalid input',
  ),
)

6. Validation and Verification of TextField

You can add a simple validation logic to check the validity of the text entered by the user. For example, you can perform basic validation for email format:

String? validateEmail(String? value) {
  if (value == null || value.isEmpty) {
    return 'Please enter an email';
  }
  String pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$';
  RegExp regex = RegExp(pattern);
  if (!regex.hasMatch(value)) {
    return 'Invalid email format';
  }
  return null;
}

TextField(
  decoration: InputDecoration(
    errorText: validateEmail(myController.text),
  ),
)

7. TextField and TextFormField

To handle input field validation and state management more efficiently, you can use the TextFormField widget. The TextFormField is used with the Form widget, providing better validation and state management:

Form(
  child: Column(
    children: [
      TextFormField(
        decoration: InputDecoration(labelText: 'Enter your email'),
        validator: validateEmail,
      ),
      ElevatedButton(
        onPressed: () {
          // submit logic
        },
        child: Text('Submit'),
      ),
    ],
  ),
)

8. TextField and FocusNode

You can use a FocusNode to control focus on the input field. Controlling focus allows you to perform specific actions or manage input:

FocusNode myFocusNode = FocusNode();

@override
void initState() {
  super.initState();
  myFocusNode.addListener(() {
    print('Focus state changed: ${myFocusNode.hasFocus}');
  });
}

@override
Widget build(BuildContext context) {
  return TextField(
    focusNode: myFocusNode,
    decoration: InputDecoration(labelText: 'Test focus here'),
  );
}

9. Conclusion

In this tutorial, we took an in-depth look at the TextField widget in Flutter. The TextField is a very basic UI component that is essential in any app that requires user input. By utilizing various properties and features, we can provide a more intuitive and useful user experience.

While developing mobile and web applications using Flutter, you can enhance the user interface by leveraging the various uses of TextField. I hope this tutorial has been helpful for your Flutter development!

Flutter Course: 11.3 Applying flutter_animate

Flutter is a powerful framework for developing various multimedia applications. User interface (UI) animations play a crucial role in enhancing user experience and improving the overall quality of the app. In this tutorial, we will explore how to apply animations to Flutter applications using the flutter_animate package.

1. What is flutter_animate?

flutter_animate is a package that helps implement animations easily in Flutter. With this package, you can easily apply various animation effects and it provides features that reduce the complexity of animations, allowing developers to work more efficiently.

2. Installing the flutter_animate package

First, you need to include the flutter_animate package in your project. To do this, you should add the package in the pubspec.yaml file.

dependencies:
  flutter:
    sdk: flutter
  flutter_animate: ^2.0.0  # Enter the required version here.

After that, use the command below to install the package.

flutter pub get

3. Basic usage

After installing the package, let’s look at how to use flutter_animate through a simple example.

3.1 Basic animation effects

The following example code shows a simple Fade-In animation.

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

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Animate Example'),
      ),
      body: Center(
        child: Container(
          child: Text('This text uses a fade-in animation.')
              .animate()
              .fadeIn(duration: 1.seconds),
        ),
      ),
    );
  }
}

The above code shows the effect of text gradually appearing over 1 second. After calling the .animate() method, you can chain and apply the desired animation.

3.2 Applying various animations

The flutter_animate package supports various animations. Here are some animation techniques:

  • .fadeIn() – The element appears gradually.
  • .fadeOut() – The element disappears gradually.
  • .scale() – Changes the size of the element.
  • .slide() – Moves the element from one side of the screen to the other.
  • .rotate() – Rotates the element.

Here’s an example of using multiple animations together.

class AnimatedMultiEffect extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Multiple Animation Effects'),
      ),
      body: Center(
        child: Container(
          child: Text('Multiple animation effects are applied!')
              .animate()
              .fadeIn(duration: 1.seconds)
              .scale(begin: 0.5, end: 1.0)
              .slideX(begin: -1.0, end: 0.0)
              .rotate(begin: 0.0, end: 1.0)
              .start(delay: 300.milliseconds),
        ),
      ),
    );
  }
}

4. Customizing animations

You can adjust the properties of animations using flutter_animate. For example, you can specify the duration, starting point, and end point of the animation for finer adjustments. Below is how to customize an animation.

class CustomAnimationExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Custom Animation Example'),
      ),
      body: Center(
        child: Container(
          child: Text('You can change the properties of the animation!')
              .animate()
              .fadeIn(duration: 2.seconds, curve: Curves.easeIn)
              .scale(begin: 0.0, end: 1.5, duration: 2.seconds)
              .start(),
        ),
      ),
    );
  }
}

In the above example, we added a Curves.easeIn curve to the fadeIn animation to make it appear smoothly. We also set the duration of the scale animation to control the flow of the animation.

5. Real-life examples of using animations

5.1 Adding animation to a button

To provide feedback on user interactions, you can apply animations to buttons, creating a more engaging UI. Below is an example of adding animation effects when clicking a button.

class AnimatedButton extends StatefulWidget {
  @override
  _AnimatedButtonState createState() => _AnimatedButtonState();
}

class _AnimatedButtonState extends State {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Animated Button Example'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            // Animation trigger
            setState(() {});
          },
          child: Text('Animation Button')
              .animate()
              .scale(begin: 1.0, end: 1.5)
              .fadeIn(duration: 0.5.seconds)
              .slideY(begin: -1.0, end: 0.0)
              .start(),
        ),
      ),
    );
  }
}

5.2 Applying animation to list items

Applying animations to list items can provide users with a more dynamic UI. For example, you can add a fade-in effect every time a new item is added to the list.

class AnimatedListExample extends StatelessWidget {
  final List items = ["Item 1", "Item 2", "Item 3"];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Animated List Example'),
      ),
      body: ListView.builder(
        itemCount: items.length,
        itemBuilder: (context, index) {
          return Container(
            padding: EdgeInsets.all(8.0),
            child: Text(items[index])
                .animate()
                .fadeIn(duration: 0.5.seconds)
                .start(),
          );
        },
      ),
    );
  }
}

6. Optimization and performance

Animations can make the UI more attractive, but applying too many animations can lead to performance degradation. Therefore, it is important to follow the optimization guidelines below.

  • Minimize the number of animations to help users focus.
  • Remove unnecessary animations to improve performance.
  • Analyze animation performance in debug mode to identify problem areas.
  • Adjust the size and duration of animations to maintain optimal performance.

7. Conclusion

In this tutorial, we explored how to apply animations to Flutter applications using the flutter_animate package. Animations can enhance user experience and create a more attractive UI. Be sure to actively utilize animations in your future projects!

If you want more detailed information, please visit here. If you have any questions or needs, feel free to leave a comment. Thank you!

Flutter Course: 11.2 Layout Composition

Flutter is a powerful framework that allows you to easily create attractive User Interfaces (UI). In this lesson 11.2, we will delve deeply into the layout composition in Flutter. Layout composition is a crucial process for effectively placing complex UI elements. In this process, we will utilize various widgets and the layout system provided by Flutter.

1. Fundamental Concepts of Layout

Layout refers to the topic of how UI elements are arranged on the screen. Flutter uses widgets to compose layouts. Everything in Flutter is made up of widgets, and these widgets combine to form complex UIs. The layout system primarily uses containers, so the properties of the container determine the position of the UI.

1.1. Concept of Widgets

In Flutter, widgets are the components of the UI, and each widget has its own properties and layout. Flutter’s widgets are broadly categorized into StatelessWidget and StatefulWidget. StatelessWidget does not have a state and is used to create unchanging UIs. Conversely, StatefulWidget has a state and changes the UI according to state changes.

1.2. Parent-Child Relationship

Widgets have a parent-child relationship. A parent widget can include child widgets, enabling layout composition. For example, the Column widget can arrange multiple child widgets vertically.

2. Layout Widgets in Flutter

Flutter provides various layout widgets. In this section, we will look at the key layout widgets that are commonly used.

2.1. Container

The Container widget is the most basic widget and allows you to encapsulate other widgets to adjust size, padding, margins, etc. You can apply additional styles such as background color and borders using the Container.

Container(
  width: 200,
  height: 100,
  color: Colors.blue,
  padding: EdgeInsets.all(10),
  child: Text('Hello, Flutter!'),
)

2.2. Row

The Row widget arranges child widgets horizontally. You can define alignment methods using the mainAxisAlignment and crossAxisAlignment properties.

Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    Icon(Icons.star),
    Icon(Icons.star),
    Icon(Icons.star),
  ],
)

2.3. Column

The Column widget lists child widgets vertically. Similarly, you can adjust arrangements through mainAxisAlignment and crossAxisAlignment.

Column(
  mainAxisAlignment: MainAxisAlignment.start,
  children: [
    Text('Item 1'),
    Text('Item 2'),
    Text('Item 3'),
  ],
)

2.4. Stack

The Stack widget stacks multiple widgets on top of each other. You can adjust the position of each child widget using the Positioned widget.

Stack(
  children: [
    Container(color: Colors.red, width: 100, height: 100),
    Positioned(
      left: 20,
      top: 20,
      child: Container(color: Colors.blue, width: 50, height: 50),
    ),
  ],
)

2.5. ListView

The ListView widget creates a scrollable list. It is very useful for easily listing multiple items.

ListView(
  children: [
    ListTile(title: Text('Item 1')),
    ListTile(title: Text('Item 2')),
    ListTile(title: Text('Item 3')),
  ],
)

3. Layout Properties

We will learn about the main properties that can be used when configuring layout widgets.

3.1. Padding

You can use the Padding widget to add padding to a widget, setting space around child widgets.

Padding(
  padding: EdgeInsets.all(16.0),
  child: Text('Hello, Flutter!'),
)

3.2. Margin

Margins can be set as properties of the Container widget. This property expands the space around the child widget.

Container(
  margin: EdgeInsets.all(20),
  child: Text('Hello with Margin!'),
)

4. Composing Complex Layouts

Now, let’s create more complex layouts from simple widget compositions. We will combine multiple widgets to form a more realistic UI.

4.1. Creating a Card Layout

Let’s create a simple layout displaying information using a card. We will combine various widgets to create a UI that includes all elements.

Card(
  child: Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Padding(
        padding: const EdgeInsets.all(16.0),
        child: Text('Title', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
      ),
      Padding(
        padding: const EdgeInsets.all(16.0),
        child: Text('This is a sample card in Flutter.', style: TextStyle(fontSize: 16)),
      ),
      ButtonBar(
        children: [
          TextButton(child: Text('EDIT'), onPressed: () {/*Edit logic*/}),
          TextButton(child: Text('DELETE'), onPressed: () {/*Delete logic*/}),
        ],
      ),
    ],
  ),
)

5. Composing Responsive Layouts

It is also essential to create responsive layouts to allow users to use the application on various screen sizes.

5.1. Using MediaQuery

In Flutter, you can use MediaQuery to dynamically detect the size of the screen. This allows you to apply designs suitable for various screen sizes.

final width = MediaQuery.of(context).size.width;
final height = MediaQuery.of(context).size.height;

5.2. LayoutBuilder

The LayoutBuilder widget receives specific constraints for the child widget. Through this, you can configure it to act differently based on the widget’s size.

LayoutBuilder(
  builder: (BuildContext context, BoxConstraints constraints) {
    return Container(
      width: constraints.maxWidth < 600 ? 100 : 200,
      height: 100,
      child: Text('Responsive Container'),
    );
  },
)

6. Conclusion

In this lesson, we learned about layout composition in Flutter. We saw how to create complex UIs using various widgets and how responsive design can enhance user experience. By understanding and utilizing the layout system, we can develop better applications.