Flutter Cubit Implementation Listview

155 views Asked by At

i try to program a small app to track my cost spendings. As state management I use Cubit. As I am a flutter beginner, I wanted to verify if I implemented it correctly.

The UI consists of 2 screens: 1 for the cost entry, 1 for the listing of the entered expenses. Concerning architecture, I followed the cubit guidelines: UI - Cubit - Repository. The repository only consists of a list and the data is represented by cost-objects.

The classes interact like:

  1. UI calls Method in the Cubit class
  2. The Cubit class emits a state, which holds data
  3. Depending on the state the data from the cubit are used in the UI (state...)

Some questions and ideas are included as comments

main.dart

import 'package:easy_cost_splitter/screens/CostPage.dart';
import 'package:easy_cost_splitter/screens/ListPage.dart';
import 'package:flutter/material.dart';
import 'package:easy_cost_splitter/repositories/FakeLocalRepository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'logic/cubit/manage_cost_cubit.dart';

void main() {
  runApp(MainApp());
}

class MainApp extends StatelessWidget {
  MainApp({super.key});

  final fakeLocalRepository = FakeLocalRepository();

  @override
  Widget build(BuildContext context) {
    return BlocProvider( // I PUT IT ON TOP OF THE APP, IN ORDER TO HAVE ACCESS FROM ALL SCREENS

      create: (context) => ManageCostCubit(fakeLocalRepository),
      child: Builder(builder: (context) {
        return MaterialApp(
          routes: {
            "/CostPage": (context) => const CostPage(),
            "/ListPage": (context) => const ListPage(),
          },

          initialRoute: "/CostPage",

          theme: ThemeData(
            primarySwatch: Colors.amber,
          ),
          home: const ListPage(), 
        );
      }),
    );
  }
}


Class CostList:

import 'package:easy_cost_splitter/logic/cubit/manage_cost_cubit.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import '../data/models/cost.dart';

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

  @override
  State<ListPage> createState() => _ListPageState();
}

class _ListPageState extends State<ListPage> {
  late List<Cost> costList;

  @override
  void initState() {
  
    super.initState();
    final costCubit = context.read<ManageCostCubit>();
// I PUT THE CONNECTION OF THE WIDGET TO THE CUBIT IN THE INITSTATE-METHOD. WHEN I PUT IT INSIDE THE BUILD METHOD, IT UPDATED EVERY FRAME

    costCubit.getAllCost();
// WHEN BUILDING THE SCREEN INITIALLY, TO GET A COST-LIST. I AM NOT SURE IF THIS IS RIGHT HERE. 

  }

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<ManageCostCubit, ManageCostState>(
        builder: (context, state) {

      if (state is ManageCostSuccess) {
        costList = state.costs;
      } else {
        costList = [];
      }

      return Scaffold(
        appBar: AppBar(
          title: const Text("Easy Cost Splitter"),
        ),
        body: SafeArea(
          child: Center(
            child: state is ManageCostLoading
                ? CircularProgressIndicator() 
// THE CICULARPROGRESSINDICATOR ONLY WORKS HERE CORRECTLY, WHEN I PUT IT ABOVE THE SCAFFOLD AND PUT IT IN A CONSTRAINED BOX, IT STILL COVERS THE WHOLE SCREEN 

                : Column(
                    children: [
                      ListView.builder(
                        
                        padding: const EdgeInsets.all(20),
                        

                        itemCount:
                            state is ManageCostSuccess ? state.costs.length : 0,
                        // WHY IS IT NOT POSSIBLE TO MAKE A COMPLEX "IF () ELSE ..." REQUEST HERE??? 


                        scrollDirection: Axis
                            .vertical, 
                        shrinkWrap:
                            true, 
                        itemBuilder: ((context, index) {
                          return ListTile(
                            title: Text(costList[index].item),
                            subtitle: Text(costList[index].cost.toString()),
                            onTap: () {
                              // I CAN NOT CALL THE "costCubit." OBJECT FROM THE INIT-STATE METHOD, WHY??

                            },
                          );
                        }),
                      ),
                      const SizedBox(
                        height: 40,
                      ),
                      ElevatedButton(
                          onPressed: () {
                            Navigator.pushNamed(context, "/CostPage");
                          },
                          child: const Text('Seite Wechseln')),
                    ],
                  ),
          ),
        ),
      );
    });
  }
}

Class Cubit

import "package:easy_cost_splitter/repositories/FakeLocalRepository.dart";
import "package:equatable/equatable.dart";
import "package:flutter_bloc/flutter_bloc.dart";
import "package:easy_cost_splitter/data/models/cost.dart";


part "manage_cost_state.dart";

class ManageCostCubit extends Cubit<ManageCostState> {
  FakeLocalRepository fakeLocalRepository;

  // ManageCostCubit(this.fakeLocalRepository) : super(ManageCostInitial());
  ManageCostCubit(this.fakeLocalRepository) : super(ManageCostInitial());

  Future<void> getAllCost() async {
    emit(ManageCostLoading());
    await Future.delayed(Duration(seconds: 3));

    try {
      final costs = await fakeLocalRepository.getAllCost();
      emit(ManageCostSuccess(costs: costs));
      print("in ManageCostCubit.getAllCost: " + state.toString());
    } on Exception {
      emit(ManageCostError());
    }
  }

  Future<void> addCost(String sIn, double iIn) async {
    emit(ManageCostLoading());

    Cost cIn = new Cost(sIn, iIn);
    print(state.toString());
    try {
      fakeLocalRepository.addCost(cIn);
      final costs = await fakeLocalRepository.getAllCost();
      // IN ORDER TO BE ABLE TO EMIT THE SUCCESS-CLASS I NEED A LIST OF COSTS TO PASS IT TO THE CONSTRUCTOR; DON'T KNOW IF THIS IS GOOD STYLE?

      emit(ManageCostSuccess(costs: costs));
      print(state.toString());
      print(costs.last.item);
    } on Exception {
      emit(ManageCostError());
    }
  }
}


Class State

part of "manage_cost_cubit.dart";

abstract class ManageCostState extends Equatable {
  const ManageCostState();

  @override
  List<Object> get props => [];
}

class ManageCostInitial extends ManageCostState {}

class ManageCostLoading extends ManageCostState {}

class ManageCostSuccess extends ManageCostState {
  final List<Cost> costs;
  const ManageCostSuccess({required this.costs});
  @override
  List<Object> get props => [costs];
}

class ManageCostError extends ManageCostState {}


0

There are 0 answers