My map doesn't update after I add a new item through the putifAbsent method

Issue

My map doesnt take any new values that I add to it.
This is my Provider:

import 'package:flutter/material.dart';

import '../classes/transaction.dart';




class Transactions with ChangeNotifier {

  Map<String, Transaction> _userTransactions = {

   'ju': Transaction(amount: 45, date: DateTime.now(), title: 'socks', accountType: 'timr', notes: 'joa' , icon: Icon(Icons.today,), id: 'kololdcd', repeat: 'always')
  } ;
  
   
 Map<String, Transaction> get userTransactions {
   print (_userTransactions);
   return {..._userTransactions};
   
 }
 


   void addNewTransaction({String txTitle, double txAmount, DateTime chosenDate,
      String txAccountType, String txNotes, String txRepeat, Icon txIcon, String txId}) {
     
    
    if (txRepeat != 'Einmalig' && _userTransactions.containsKey(txId)){
    _userTransactions.update(txId, (existingItem) =>  Transaction(
      title: existingItem.title,
      amount: existingItem.amount,
      date: DateTime.now(),
      id: DateTime.now().toString(),
      accountType: existingItem.accountType,
      notes: existingItem.notes,
      repeat: existingItem.repeat,
      icon: existingItem.icon,
    ) );
    
    } else {
      _userTransactions.putIfAbsent(txId, ()=>  Transaction(
      title: txTitle,
      amount: txAmount,
      date: chosenDate,
      id: DateTime.now().toString(),
      accountType: txAccountType,
      notes: txNotes,
      repeat: txRepeat,
      icon: txIcon,
    ));

    }
    //final cant be changed anymore
    print (_userTransactions);
    notifyListeners();
    
  }
  
  
 


}

This is my listener:

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

import '../providers/transactions.dart';

import 'package:intl/intl.dart';


class TransactionList extends StatelessWidget {
 

  @override
  Widget build(BuildContext context) {
   final transactionsData = Provider.of<Transactions>(context);
   final transactions = transactionsData.userTransactions;
   print (transactions);
 
    return transactionsData.userTransactions.isEmpty
        ? LayoutBuilder(builder: (ctx, constraints) {
            return Column(
              children: <Widget>[
                Text(
                  'Keine Transaktionen hinzugefügt. ',
                  style: Theme.of(context).textTheme.title,
                ),
                const SizedBox(height: 20),
                Container(
                  height: constraints.maxHeight * 0.6,
                
                ),
              ],
            );
          })
        : 
       
                 
                                     ListView.builder(
                 itemCount: transactions.length,     
                 itemBuilder:(context, i) {
                    String key = transactions.keys.elementAt(i);
                   return Card(
      color: Colors.transparent,
      elevation: 0,
      margin: const EdgeInsets.symmetric(
        vertical: 8,
        horizontal: 5,
      ),
      child: ListTile(
        leading: CircleAvatar(
          backgroundColor: Colors.transparent,
          radius: 30,
          child: Padding(
            padding: const EdgeInsets.all(6),
            child: FittedBox(
              child: Text('\$${transactions[key].amount}'),
            ),
          ),
        ),
        title: Text(
          transactions[key].title,
          style: Theme.of(context).textTheme.title,
        ),
        subtitle: Text(
           DateFormat.yMMMd().format(transactions[key].date),
        ),
      
      ),

    );}
                  );
  }
}

I am calling this provider method from an other screen:

void _submitData() {
    
    if (_amountController.text.isEmpty) {
      return;
    }

    final enteredTitle = _titleController.text;
    final enteredAmount = double.parse(_amountController.text);
    final enteredAccount = _accountController.text;
    final enteredNotes = _notesController.text;
    final enteredRepeat = _repeatController.text;
    final enteredIcon = Icon(MyIcon.paying);
    var uuid = Uuid(options: {});

    if (enteredTitle.isEmpty ||
        enteredAmount <= 0 ||
        _selectedDate == null ||
        enteredAccount.isEmpty ||
        enteredRepeat.isEmpty) {
      return; 
    } else {
      Transactions().addNewTransaction(
        txTitle: enteredTitle,
        txAmount: enteredAmount,
        chosenDate: _selectedDate,
        txAccountType: enteredAccount,
        txNotes: enteredNotes,
        txRepeat: enteredRepeat,
        txIcon: enteredIcon,
        txId: uuid.v1(options: {
          'node': [0x01, 0x23, 0x45, 0x67, 0x89, 0xab],
          'clockSeq': 0x1234,
          'mSecs': DateTime.now().millisecondsSinceEpoch,
          'nSecs': 5678
        }),
      );
    }
    Navigator.of(context).pop(context);
   
  }

If I add a new pair to the map, it is shown in the addTransactionMethod (print Statement gives me the values of the new item). But as you can see in the print statement of the userTransactions my map _userTransactions doesnt take the new value, it only keeps the predifened one that I hardcoded.

This is the Debugger Konsole (the first print Statement is from the addTransaction Methode – the second one is directly from the userTransaction map:

I/flutter ( 5636): {ju: { kololdcd,socks,2020-02-14 22:33:09.616563, timr,joa,always,Icon(IconData(U+0E8DF)),45.0}, ho: { 2020-02-14 22:33:09.621559,Why not,2019-02-11 00:00:00.000, jes,cool,always,Icon(IconData(U+0E80D)),78.0}}

I/flutter ( 5636): {ju: { kololdcd,socks,2020-02-14 22:32:14.896756, timr,joa,always,Icon(IconData(U+0E8DF)),45.0}}

As you can see the Second item is missing in the second print.

As you can see in the picture the new value sin’t added to the map

How can I solve the issue? I just want to add items to this _userTransactions.

Would love to hear some suggestions 🙂

Solution

The problem is your class widget is a StatelessWidget. You can’t update a StatelessWidget because being able to update it would mean it has a state that can be updated. For your approach, you will have to change your widget to being stateful:

class TransactionList extends StatefulWidget {
  TransactionList({Key key}) : super(key);

  @override
  State get createState => _TransactionListState();
}

class _TransactionListState extends State<TransactionList> {
  @override
  Widget build(BuildContext context) {
    ...
  }
}

An alternative solution which would allow you to leave your widget as a StatelessWidget (which is more recommended anyway) is that instead of calling Provider.of directly, you use a Consumer to automatically pull the provider data to a builder method. The Consumer will also handle the rebuilding of your widget in the event that your transactions object gets updated.

class TransactionList extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<Transactions>(
      builder: (context, transactionsData, _) {
        ...
      },
    );
  }
}

Answered By – Abion47

Answer Checked By – David Goodson (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published.