flutterdartexceptioncallbacktypeerror

Exception: _TypeError type 'Null' is not a subtype of type '(Quote) => dynamic'


I am new to Flutter. I am building an app to display some text and then create a button to remove it. I have the three following dart files- 1. main.dart

// ignore_for_file: prefer_const_literals_to_create_immutables, prefer_const_constructors_in_immutables,         use_key_in_widget_constructors

import 'package:flutter/material.dart';
import 'quote.dart';
import 'QuoteCard.dart';

void main() => runApp(MaterialApp(
  home: QuoteList()
));

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

  @override
  State<QuoteList> createState() => QuoteListState();
}

class QuoteListState extends State<QuoteList> {

  List<Quote> quotes = [
  Quote(text: 'hello', author: 'Pete'),
  Quote(text: 'hi', author: 'Bill'),
  Quote(text: 'hey', author: 'Tom')
  ];

  delete (Quote quote) {
    setState(() {
      quotes.remove(quote);
    });
  } 

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: Text('quotes'),
        centerTitle: true,
        backgroundColor: Colors.redAccent,
      ),
      body: Column(
        children: quotes.map((quote) => QuoteCard(
          quote: quote,
          onRemoveData: delete(quote),
        )).toList(),
    ) );
  }
}


2. QuoteCard.dart

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

class QuoteCard extends StatelessWidget {
  
  final Quote quote;
  final Function(Quote) onRemoveData;
  QuoteCard({required this.quote, required this.onRemoveData});
  @override
  Widget build(BuildContext context) {
    return Card(
      margin: EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0.0),
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Text(
              quote.text,
              style: TextStyle(
                fontSize: 18.0,
                color: Colors.grey[800]
              ),
            ),
            SizedBox(height: 6.0,),
            Text(
              quote.author,
              style: TextStyle(
                fontSize: 10.0,
                color: Colors.grey[700]
              ),
            ),
            SizedBox(height: 8.0,),
            TextButton.icon(
              onPressed: onRemoveData(quote),
              label: Text('delete quote'),
              icon: Icon(Icons.delete),
            )
          ],
        ),
      ),
    );
  }
}

3. quote.dart

class Quote {
  String text;
  String author;
  Quote({required this.text, required this.author});
}

When I run the application on vscode, I get this exception Exception has occured _TypeError (type 'Null' is not a subtype of type '(Quote) => dynamic')

I wanted to remove the quote using the button, at first I created a simple delete function, then it gave me an exception. I figured out I needed to create a callback (onRemoveData). I did that and now I get this error


Solution

  • Here my solution for your problem, explanation in the code comments:

    class QuoteList extends StatefulWidget {
      const QuoteList({super.key});
    
      @override
      State<QuoteList> createState() => _QuoteListState();
    }
    
    class _QuoteListState extends State<QuoteList> {
      List<Quote> quotes = [
        Quote(text: 'hello', author: 'Pete'),
        Quote(text: 'hi', author: 'Bill'),
        Quote(text: 'hey', author: 'Tom')
      ];
    
      delete(Quote quote) {
        setState(() {
          quotes.remove(quote);
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            backgroundColor: Colors.white,
            appBar: AppBar(
              title: const Text('quotes'),
              centerTitle: true,
              backgroundColor: Colors.redAccent,
            ),
            body: Column(
              children: quotes
                  .map((quote) => QuoteCard(
                        quote: quote,
                        onRemoveData: (value) { // <---- you have to pass the parameter value which is the quote inside card to then call the callback, you were passing the actual quote from the quotes array
                          delete(value);
                        },
                      ))
                  .toList(),
            ));
      }
    }
    
    class QuoteCard extends StatelessWidget {
      final Quote quote;
      final Function(Quote) onRemoveData;
      const QuoteCard({super.key, required this.quote, required this.onRemoveData});
      @override
      Widget build(BuildContext context) {
        return Card(
          margin: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0.0),
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                Text(
                  quote.text,
                  style: TextStyle(fontSize: 18.0, color: Colors.grey[800]),
                ),
                const SizedBox(
                  height: 6.0,
                ),
                Text(
                  quote.author,
                  style: TextStyle(fontSize: 10.0, color: Colors.grey[700]),
                ),
                const SizedBox(
                  height: 8.0,
                ),
                TextButton.icon(
                  onPressed: () {
                    onRemoveData(quote); // <---- also here onPress is a void callback so it cannot be passed a function that expects a parameter
                  },
                  label: const Text('delete quote'),
                  icon: const Icon(Icons.delete),
                )
              ],
            ),
          ),
        );
      }
    }
    
    class Quote {
      String text;
      String author;
      Quote({required this.text, required this.author});
    }