Pull refresh in una ListView in Flutter

Mattepuffo's logo
Pull refresh in una ListView in Flutter

Pull refresh in una ListView in Flutter

In sostanza quello che vogliamo ottenere è un refresh della nostra ListView in Flutter quando eseguiamo una "pull" della schermata.

Per dirla in parole povere, quando la tiriamo giù.

Il punto di partenza è questo articolo, in cui abbiamo visto come filtrare la ListView.

Il punto di partenza è sempre il nostro model:

class Book {
  final int? id;
  final String? title;
  final int? authorId;
  final String? author;
  final int? editorId;
  final String? editor;
  final double? price;
  final String? isbn;
  final String? note;
  final int? scaffale;
  final DateTime? dataAggiunta;

  Book({
    this.id,
    this.title,
    this.authorId,
    this.author,
    this.editorId,
    this.editor,
    this.price,
    this.isbn,
    this.note,
    this.scaffale,
    this.dataAggiunta,
  });
}

Questo il nostro widget con la ListTile:

import 'package:flutter/material.dart';

class BookItem extends StatelessWidget {
  final String? title;
  final String? author;
  final double? price;

  const BookItem({
    super.key,
    this.title,
    this.author,
    this.price,
  });

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ListTile(
          shape: RoundedRectangleBorder(
            side: BorderSide(
              color: Theme.of(context).primaryColor,
              width: 1,
            ),
            borderRadius: BorderRadius.circular(5),
          ),
          leading: CircleAvatar(
            radius: 20,
            // backgroundColor: Colors.purple,
            child: Padding(
              padding: const EdgeInsets.all(6),
              child: FittedBox(
                child: Text(
                  '€ $price',
                  style: Theme.of(context).textTheme.bodyText1,
                ),
              ),
            ),
          ),
          title: Text(
            title!,
            style: Theme.of(context).textTheme.bodyText1,
          ),
          subtitle: Text(author!),
          trailing: MediaQuery.of(context).size.width > 460
              ? OutlinedButton.icon(
                  icon: const Icon(Icons.delete),
                  label: const Text('Cancella'),
                  onPressed: () => {},
                )
              : IconButton(
                  icon: const Icon(Icons.delete),
                  color: Theme.of(context).errorColor,
                  onPressed: () => {},
                ),
        ),
        const SizedBox(
          height: 5,
        ),
      ],
    );
  }
}

A questo punto vediamo la schermata:

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

import 'models/book.dart';
import 'widgets/book_item.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MP Book',
      theme: ThemeData(
        fontFamily: 'Raleway',
        // brightness: Brightness.dark,
        primarySwatch: Colors.amber,
        textTheme: const TextTheme(
          // headline1: TextStyle(fontWeight: FontWeight.bold),
          headline6: TextStyle(fontWeight: FontWeight.bold),
          bodyText1: TextStyle(fontSize: 14.0, fontFamily: 'Hind',color: Colors.black),
        ),
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: ElevatedButton.styleFrom(
            backgroundColor: Colors.purple,
            foregroundColor: Colors.white,
          ),
        ),
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final TextEditingController _searchController = TextEditingController();

  final List<Book> _bookList = [
    Book(
      id: 1,
      title: 'IT',
      authorId: 2,
      author: 'Stephen King',
      editor: 'Mondadori',
      editorId: 2,
      price: 33.25,
      isbn: '',
      scaffale: 2,
      note: '',
      dataAggiunta: DateTime.now(),
    ),
    Book(
      id: 2,
      title: 'Sahara',
      authorId: 2,
      author: 'Clive Cussler',
      editor: 'Mondadori',
      editorId: 2,
      price: 23.05,
      isbn: '',
      scaffale: 2,
      note: '',
      dataAggiunta: DateTime.now(),
    ),
    Book(
      id: 3,
      title: 'Mars',
      authorId: 2,
      author: 'Weir',
      editor: 'Mondadori',
      editorId: 2,
      price: 90.25,
      isbn: '',
      scaffale: 2,
      note: '',
      dataAggiunta: DateTime.now(),
    ),
    Book(
      id: 3,
      title: 'Libro 4',
      authorId: 5,
      author: 'Stephen King',
      editor: 'Mondadori',
      editorId: 2,
      price: 33.25,
      isbn: '',
      scaffale: 2,
      note: '',
      dataAggiunta: DateTime.now(),
    ),
  ];
  final List<Book> _copyList = <Book>[];

  @override
  void initState() {
    _copyList.addAll(_bookList);
    super.initState();
  }

  void cercaLibro(String query) {
    if (query.isNotEmpty) {
      List<Book> searchList = <Book>[];
      for (Book book in _bookList) {
        if (book.title!.toLowerCase().contains(query.toLowerCase()) ||
            book.author!.toLowerCase().contains(query.toLowerCase())) {
          searchList.add(book);
        }
      }
      setState(() {
        _copyList.clear();
        _copyList.addAll(searchList);
      });
      return;
    } else {
      setState(() {
        _copyList.clear();
        _copyList.addAll(_bookList);
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('MP Book'),
        // backgroundColor: Colors.amber,
      ),
      body: Column(
        children: <Widget>[
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: TextField(
              onChanged: (value) {
                cercaLibro(value);
              },
              controller: _searchController,
              decoration: const InputDecoration(
                labelText: "Cerca...",
                hintText: "Cerca...",
                prefixIcon: Icon(Icons.search),
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.all(
                    Radius.circular(5),
                  ),
                ),
              ),
            ),
          ),
          Expanded(
            child: RefreshIndicator(
              displacement: 150,
              backgroundColor: Colors.black38,
              strokeWidth: 3,
              triggerMode: RefreshIndicatorTriggerMode.onEdge,
              onRefresh: () async {
                _searchController.text = '';
                await Future.delayed(const Duration(milliseconds: 1500));
                setState(() {
                  _copyList.clear();
                  _copyList.addAll(_bookList);
                });
              },
              child: ListView.builder(
                padding: const EdgeInsets.all(10.0),
                itemCount: _copyList.length,
                physics: const AlwaysScrollableScrollPhysics(),
                itemBuilder: (ctx, i) => BookItem(
                  title: _copyList[i].title,
                  author: _copyList[i].author,
                  price: _copyList[i].price,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Come vedete abbiamo messo la nostra ListView dentro ad RefreshIndicator.

Ed abbiamo anche aggiunto l'opzione AlwaysScrollableScrollPhysics.

Enjoy!


Condividi

Commentami!