Construindo aplicação Web com Flutter

Tempo de leitura: 5 minutes

Configurando o ambiente

Certifique-se de estar com a versão ≥ 3.56.0 da extensão Flutter

flutter upgrade

Criando o projeto

Flutter: New Web Project

Para rodar na web, no canto final existe o padrão de dispositivo, selecione Chrome web ou Edge web

Veja ele executando o exemplo abaixo.

Códido Fonte do Exemplo acima.

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Primeira aplicação com Flutter', // title da página
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        // ignore: avoid_unnecessary_containers
        home: Container(
          child: const Center(
            child: CounterWidget(),
          ),
        ));
  }
}

class CounterWidget extends StatefulWidget {
  const CounterWidget({super.key});

  @override
  // ignore: library_private_types_in_public_api
  _CounterWidgetState createState() => _CounterWidgetState();
}

class _CounterWidgetState extends State<CounterWidget> {
  int _counter = 10;

  Future<void> _fimAlert(BuildContext context) {
    return showDialog<void>(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('Alerta'),
          content: const Text('O contador será reiniciado'),
          actions: <Widget>[
            TextButton(
              child: const Text('Ok'),
              onPressed: () {
                setState(() {
                  _counter = 10;
                });
                Navigator.of(context).pop();
              },
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {

    final ButtonStyle flatButtonStyle = TextButton.styleFrom(
      foregroundColor: Colors.amber,
    );

    // ignore: avoid_unnecessary_containers
    return Container(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text(
            "$_counter",
            style: const TextStyle(color: Colors.grey, fontSize: 40.0),
          ),
          TextButton(
            style: flatButtonStyle,
            onPressed: () {
              setState(() {
                _counter--;
                if (_counter == 0) _fimAlert(context);
              });
            },
            child: const Text('Decrementar'),
          )
        ],
      ),
    );
  }
}

Vamos ao To Do

import 'package:flutter/material.dart';

class TodoPage extends StatefulWidget {
  @override
  _TodoPageState createState() => _TodoPageState();
}

class _TodoPageState extends State<TodoPage> {
  //Controllers são usadas quando precisamos resgatar ou modificar uma propriedade de um widget
  final _textController = TextEditingController();

  //Lista responsável por popular nosso ListView
  List _toDoList = [];

  //Caso o usuário remover uma tarefa, ela ficará em memória nesta variável por um tempo
  Map<String, dynamic>? _lastRemoved;

  int? _lastRemovedPos;

  void addToDo() {
    setState(() {
      Map<String, dynamic> newTodo = Map();
      newTodo["title"] = _textController.text;
      newTodo["ok"] = false;
      _toDoList.add(newTodo);
      _textController.text = '';
    });
  }

  void _dismissToDo(
      DismissDirection direction, BuildContext context, int index) {
    setState(() {
      _lastRemoved = Map.from(_toDoList[index]);
      _lastRemovedPos = index;
      _toDoList.removeAt(index);

      final snack = SnackBar(
        content: Text('Tarefa "${_lastRemoved!["title"]}" removida!'),
        action: SnackBarAction(
          label: "Desfazer",
          onPressed: () {
            setState(() {
              _toDoList.insert(_lastRemovedPos!, _lastRemoved);
            });
          },
        ),
        duration: const Duration(seconds: 5),
      );
      ScaffoldMessenger.of(context).showSnackBar(snack);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('ToDo Flutter'),
          backgroundColor: Colors.purple,
        ),
        body: Column(
          children: <Widget>[
            Container(
                padding: const EdgeInsets.fromLTRB(17.0, 1.0, 7.0, 1.0),
                child: Row(
                  children: <Widget>[
                    Expanded(
                        child: TextField(
                      controller: _textController,
                      decoration: const InputDecoration(
                          labelText: 'Nova tarefa',
                          labelStyle: TextStyle(color: Colors.grey)),
                    )),
                    ElevatedButton(
                      style: ElevatedButton.styleFrom(
                        foregroundColor: Colors.white,//change background color of button
                        backgroundColor: Colors.indigo,//change text color of button
                      ),
                      onPressed: addToDo,
                      child: const Text('Adicionar'),
                    ),
                  ],
                )),
            Expanded(
                flex: 2,
                child: ListView.builder(
                    padding: const EdgeInsets.only(top: 10.0),
                    itemCount: _toDoList.length,
                    itemBuilder: itemBuilder))
          ],
        ));
  }

  Widget itemBuilder(context, index) {
    //Widget responsável por permitir dismiss
    return Dismissible(
      key: Key(DateTime.now().millisecondsSinceEpoch.toString()),
      //A propriedade "background" representa o fundo da nossa tile, o fundo em si não possui ações
      //As ações estão no evento onDismissed
      background: Container(
        color: Colors.redAccent,
        child: const Align(
          alignment: Alignment(-0.9, 0.0),
          child: Icon(Icons.delete, color: Colors.white),
        ),
      ),
      direction: DismissDirection.startToEnd,
      child: CheckboxListTile(
        onChanged: (c) {
          setState(() {
            _toDoList[index]["ok"] = c;
          });
        },
        title: Text(_toDoList[index]["title"]),
        value: _toDoList[index]["ok"],
        secondary: CircleAvatar(
          child: Icon(_toDoList[index]["ok"] ? Icons.check : Icons.error),
        ),
      ),
      onDismissed: (direction) {
        _dismissToDo(direction, context, index);
      },
    );
  }
}

Após altere seu arquivo main.dart para o código abaixo

import 'package:flutter/material.dart';
import 'package:flutter_web_teste/todo.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Primeira aplicação com Flutter', // title da página
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        // ignore: avoid_unnecessary_containers
        home: TodoPage()
    );
  }
}

Vamos agora executar o código, pressione F5 para vermos o resultado. Se você estiver seguindo o tutorial a risca o resultado é para estar como no video abaixo

Um outro video com Exclusão.

É isso ai pessoal, temos uma aplicação feita em flutter web totalmente usavel e de rápida criação. Em breve lançarei alguns tutoriais mais avançados para criar web applications reais para o mercado.