Flutter: miglioriamo la nostra listview

Nell'articolo precedente abbiamo visto come costruire una semplice ListView con dati casuali di esempio. Partendo da quel progetto, lo amplieremo per migliorare l'aspetto degli elementi e per introdurre la gestione dello stato.


Un nuovo widget per gli elementi della ListView 

Nella cartella components creiamo un nuovo file circleimage.dart con questo codice:


import 'package:flutter/material.dart';

class CircleImage extends StatelessWidget {
  const CircleImage({
    Key? key,
    this.imageProvider,
    this.imageRadius = 20,
  }) : super(key: key);

  final double imageRadius;
  final ImageProvider? imageProvider;

  @override
  Widget build(BuildContext context) {
    return CircleAvatar(
      backgroundColor: Colors.black,
      radius: imageRadius,
      child: CircleAvatar(
        radius: imageRadius - 1,
        backgroundImage: imageProvider,
      ),
    );
  }
}


Useremo questo widget per aggiungere una immagine (circolare con un bordo nero di 1 pixel) ad ogni elemento della listview.

Modifichiamo anche la classe Item nel file /models/item.dart per aggiungere il nuovo campo image.


class Item {
  final String image;
  final String firstName;
  final String lastName;
  final String email;

  const Item(
      {required this.image,
      required this.firstName,
      required this.lastName,
      required this.email});
}


e il file /pages/listviewpage.dart per valorizzare sempre il nuovo campo

...

    var faker = Faker();
    for (int i = 0; i < 20; i++) {
      itemTestData.add(Item(
          image: "https://picsum.photos/140/140?random=${i}",
          firstName: faker.person.firstName(),
          lastName: faker.person.lastName(),
          email: faker.internet.email()));
    }

...

(per le immagini ho usato il servizio di generazione gratuito https://picsum.photos )

Nella cartella components modifichiamo il carditem.dart con questo codice:


import '../components/circleimage.dart';

...
  Widget build(BuildContext context) {
    return ListTile(
        leading: CircleImage(
                  imageProvider: NetworkImage(item.image),
                  imageRadius: 20),
        title: Text("${item.firstName} ${item.lastName}"),
        subtitle: Text(item.email));
  }

...

Con le modifiche appena realizzate, gli elementi della ListView, avranno più o meno un aspetto del genere:


Una nuova card per gli elementi della ListView 

Nella cartella components modifichiamo ulteriormente il carditem.dart con questo codice:


  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(3.0),
      child: Card(
        elevation: 5,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(8),
        ),
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              CircleImage(
                  imageProvider: NetworkImage(item.image), imageRadius: 20),
              const SizedBox(width: 8),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text('${item.firstName} ${item.lastName}',
                        style: const TextStyle(fontWeight: FontWeight.w700)),
                    Text(
                      item.email,
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

per ottenere un aspetto simile a questo (ombreggiatura, bordi arrotondati e in risalto):


Gestiamo lo stato: un pulsante su ogni elemento

All'elenco dei children aggiungiamo una piccola icona (un cuore rosso) su ogni elemento, che se cliccato farà apparire una notifica:

...
            children: [
              CircleImage(
                  imageProvider: NetworkImage(item.image), imageRadius: 20),
              const SizedBox(width: 8),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text('${item.firstName} ${item.lastName}',
                        style: const TextStyle(fontWeight: FontWeight.w700)),
                    Text(
                      item.email,
                    ),
                  ],
                ),
              ),
              IconButton(
                  // 4
                  icon: const Icon(Icons.favorite_border),
                  iconSize: 30,
                  color: Colors.red[400],
                  // 5
                  onPressed: () {
                    const snackBar =
                        SnackBar(content: Text('Favorite Pressed'));
                    ScaffoldMessenger.of(context).showSnackBar(snackBar);
                  }),
            ],
...

Per gestire lo stato del pulsante appena definito, facciamo un refactoring di CardItem da StatelessWidget a StatefulWidget aggiungendo la variabile _isFavorited per memorizzare lo stato corrente del pulsante :

....

class CardItem extends StatefulWidget {
  const CardItem({Key? key, required this.item}) : super(key: key);
  final Item item;

  @override
  State<CardItem> createState() => _CardItemState();
}

class _CardItemState extends State<CardItem> {
  bool _isFavorited = false;

  @override
  Widget build(BuildContext context) {
    return Padding(
...

e modifichiamo l'evento sul cuoricino, in modo che alterni due stati:

...
              IconButton(
                  icon: Icon(
                      _isFavorited ? Icons.favorite : Icons.favorite_border),
                  iconSize: 30,
                  color: Colors.red,
                  onPressed: () {
                    setState(() {
                      _isFavorited = !_isFavorited;
                    });
                    const snackBar =
                        SnackBar(content: Text('Favorite Pressed'));
                    ScaffoldMessenger.of(context).showSnackBar(snackBar);
                  }),
...

Con queste modifiche, su ogni elemento sarà possibile conservare lo stato del click sul cuoricino, che cambierà colore  

La nuova versione della nostra ListView ora è completa.

Come per gli articoli precedenti potete trovare questo codice tra le mie repo GitHub: https://github.com/luigimicco/flutter_listview_more

Commenti