Question #38EasyFlutter Basics

What is widgets in Flutter?

#flutter#widget

Answer

Overview

Widgets are the building blocks of Flutter apps. Everything you see on the screen (buttons, text, layouts, animations) is a widget. In Flutter, everything is a widget.


What is a Widget?

A widget is a description of part of the user interface. Widgets are immutable - once created, they cannot be changed.

dart
Text('Hello World')  // Text widget
ElevatedButton(      // Button widget
  onPressed: () {},
  child: Text('Click'),
)
Container(           // Layout widget
  color: Colors.blue,
  child: Text('Box'),
)

Widget Tree

Widgets are organized in a tree structure:

text
MaterialApp
└─ Scaffold
   ├─ AppBar
   │  └─ Text('Title')
   └─ Column
      ├─ Text('Hello')
      ├─ ElevatedButton
      │  └─ Text('Click')
      └─ Image

Types of Widgets

1. StatelessWidget (Immutable)

Widgets that don't change once built.

dart
class Greeting extends StatelessWidget {
  final String name;
  
  const Greeting({required this.name});
  
  
  Widget build(BuildContext context) {
    return Text('Hello, $name!');
  }
}

Examples:

  • Text
  • Icon
  • Image
  • Container (without changing properties)

Use When:

  • Display static content
  • UI doesn't change based on user interaction
  • Purely presentational

2. StatefulWidget (Mutable)

Widgets that can change over time.

dart
class Counter extends StatefulWidget {
  
  _CounterState createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int _count = 0;
  
  
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('Count: $_count'),
        ElevatedButton(
          onPressed: () => setState(() => _count++),
          child: Text('Increment'),
        ),
      ],
    );
  }
}

Examples:

  • Checkbox
  • TextField
  • Slider
  • Animated widgets

Use When:

  • User interaction changes UI
  • Data changes over time
  • Animations

Widget Categories

1. Layout Widgets

Arrange other widgets.

dart
// Row - Horizontal layout
Row(
  children: [
    Icon(Icons.star),
    Text('5.0'),
  ],
)

// Column - Vertical layout
Column(
  children: [
    Text('Title'),
    Text('Subtitle'),
  ],
)

// Stack - Overlapping widgets
Stack(
  children: [
    Image.asset('background.jpg'),
    Positioned(
      top: 20,
      left: 20,
      child: Text('Overlay'),
    ),
  ],
)

// Container - Box model
Container(
  width: 200,
  height: 100,
  color: Colors.blue,
  padding: EdgeInsets.all(16),
  child: Text('Box'),
)

Common Layout Widgets:

  • Row, Column
  • Stack
  • Container
  • Padding
  • Center
  • Align
  • Expanded
  • Flexible

2. Material Design Widgets

Pre-built Material Design components.

dart
// Scaffold - App structure
Scaffold(
  appBar: AppBar(title: Text('App')),
  body: Center(child: Text('Content')),
  floatingActionButton: FloatingActionButton(
    onPressed: () {},
    child: Icon(Icons.add),
  ),
)

// ElevatedButton
ElevatedButton(
  onPressed: () {},
  child: Text('Click Me'),
)

// Card
Card(
  child: Padding(
    padding: EdgeInsets.all(16),
    child: Text('Card Content'),
  ),
)

// ListTile
ListTile(
  leading: Icon(Icons.person),
  title: Text('John Doe'),
  subtitle: Text('john@example.com'),
  trailing: Icon(Icons.arrow_forward),
)

3. Input Widgets

Handle user input.

dart
// TextField
TextField(
  decoration: InputDecoration(
    labelText: 'Enter name',
    hintText: 'John Doe',
  ),
  onChanged: (value) => print(value),
)

// Checkbox
Checkbox(
  value: true,
  onChanged: (value) {},
)

// Switch
Switch(
  value: false,
  onChanged: (value) {},
)

// Slider
Slider(
  value: 50,
  min: 0,
  max: 100,
  onChanged: (value) {},
)

4. Display Widgets

Show content to users.

dart
// Text
Text(
  'Hello World',
  style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
)

// Image from network
Image.network('https://example.com/image.jpg')

// Image from assets
Image.asset('assets/logo.png')

// Icon
Icon(Icons.favorite, color: Colors.red, size: 32)

5. Cupertino Widgets (iOS Style)

iOS-style widgets.

dart
import 'package:flutter/cupertino.dart';

// iOS-style button
CupertinoButton(
  child: Text('iOS Button'),
  onPressed: () {},
)

// iOS-style switch
CupertinoSwitch(
  value: true,
  onChanged: (value) {},
)

// iOS-style navigation bar
CupertinoNavigationBar(
  middle: Text('iOS App'),
)

Widget Composition

Build complex UIs by composing simple widgets.

dart
class ProfileCard extends StatelessWidget {
  final String name;
  final String email;
  final String imageUrl;
  
  const ProfileCard({
    required this.name,
    required this.email,
    required this.imageUrl,
  });
  
  
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Row(
          children: [
            CircleAvatar(
              backgroundImage: NetworkImage(imageUrl),
              radius: 30,
            ),
            SizedBox(width: 16),
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  name,
                  style: TextStyle(
                    fontSize: 18,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                SizedBox(height: 4),
                Text(
                  email,
                  style: TextStyle(color: Colors.grey),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

// Usage
ProfileCard(
  name: 'John Doe',
  email: 'john@example.com',
  imageUrl: 'https://example.com/avatar.jpg
)

Widget Lifecycle

StatelessWidget Lifecycle

text
build()

Simple: just build once.


StatefulWidget Lifecycle

text
createState() → initState() → build()
  setState() → build()
  dispose()

Key Widget Properties

Common Properties

Most widgets accept these properties:

dart
Widget(
  key: Key('unique_key'),        // Unique identifier
  child: Widget(),               // Single child
  children: [Widget(), Widget()],// Multiple children
)

Container Properties

dart
Container(
  width: 200,
  height: 100,
  color: Colors.blue,
  padding: EdgeInsets.all(16),
  margin: EdgeInsets.symmetric(horizontal: 20),
  decoration: BoxDecoration(
    color: Colors.blue,
    borderRadius: BorderRadius.circular(10),
    boxShadow: [BoxShadow(color: Colors.black26, blurRadius: 10)],
  ),
  child: Text('Content'),
)

Complete Example: Login Screen

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

class _LoginScreenState extends State<LoginScreen> {
  final _emailController = TextEditingController();
  final _passwordController = TextEditingController();
  bool _isLoading = false;
  
  Future<void> _login() async {
    setState(() => _isLoading = true);
    
    // Simulate API call
    await Future.delayed(Duration(seconds: 2));
    
    setState(() => _isLoading = false);
  }
  
  
  void dispose() {
    _emailController.dispose();
    _passwordController.dispose();
    super.dispose();
  }
  
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Login')),
      body: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // Logo
            FlutterLogo(size: 100),
            SizedBox(height: 32),
            
            // Email field
            TextField(
              controller: _emailController,
              decoration: InputDecoration(
                labelText: 'Email',
                border: OutlineInputBorder(),
                prefixIcon: Icon(Icons.email),
              ),
              keyboardType: TextInputType.emailAddress,
            ),
            SizedBox(height: 16),
            
            // Password field
            TextField(
              controller: _passwordController,
              decoration: InputDecoration(
                labelText: 'Password',
                border: OutlineInputBorder(),
                prefixIcon: Icon(Icons.lock),
              ),
              obscureText: true,
            ),
            SizedBox(height: 24),
            
            // Login button
            SizedBox(
              width: double.infinity,
              child: ElevatedButton(
                onPressed: _isLoading ? null : _login,
                child: _isLoading
                    ? CircularProgressIndicator(color: Colors.white)
                    : Text('Login'),
              ),
            ),
            
            // Forgot password
            TextButton(
              onPressed: () {},
              child: Text('Forgot Password?'),
            ),
          ],
        ),
      ),
    );
  }
}

Best Practices

Important: Extract reusable widgets into separate classes

✅ Do

dart
// Good - Extract widget
class CustomButton extends StatelessWidget {
  final String text;
  final VoidCallback onPressed;
  
  const CustomButton({required this.text, required this.onPressed});
  
  
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: onPressed,
      child: Text(text),
    );
  }
}

// Good - Use const constructors
const Text('Hello')
const Icon(Icons.star)

❌ Don't

dart
// Bad - Methods returning widgets
Widget _buildButton() {
  return ElevatedButton(...);
}

// Bad - Creating widgets in build

Widget build(BuildContext context) {
  return Column(
    children: [
      for (var i = 0; i < 100; i++)
        Container(),  // ❌ Creates 100 widgets every build
    ],
  );
}

Resources