Untitled

 avatar
unknown
plain_text
a year ago
4.3 kB
3
Indexable
import 'package:flutter/cupertino.dart';

import '../../classes/event_class.dart';
import '../../constants.dart';
import '../../db/database.dart';


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

  AppDatabase get database => AppDatabase.instance;

  @override
  Widget build(BuildContext context) {

    // To continously listen to database content, it's necessary to use [StreamBuilder]
    // Stream is the function that watches [PlantTypes] table

    return StreamBuilder(
      stream: database.watchPlantTypes(),
      builder: ((context, snapshot) {

        // Snapshot data (a list of [PlantType]) are marked as 'plantTypes'
        final plantTypes = snapshot.data;
        if (plantTypes != null && plantTypes.isNotEmpty) {

    // The ListView builder uses a plantType, which is a single row from the snapshot,
    // and the 'index' paramether is needed to adress the proper query

          return ListView.builder(
            itemCount: plantTypes.length,
            itemBuilder: (context, index) {
              final plantType = plantTypes[index];

              // Single record is passed to the [PlantTypeTile] widget as 'plantType'
              return PlantTypeTile(plantType: plantType);
            },
          );

        // Null-safety check
        } else {
          return noRecordsDisplay;
        }
      }),
    );
  }
}



// =======================================================================

// [PlantTypeTile] is the building block of PlantTypeList. PlantTypes are
// fetched from the database and forwarded to the widget as 'plantType'

class PlantTypeTile extends StatelessWidget {
  final PlantType plantType;

  const PlantTypeTile({
    super.key, 
    required this.plantType
  });

  @override
  Widget build(BuildContext context) {

    // Events declaration - [EventType] is needed to determine which map should
    // be used in order to get the proper [Frequency] attributes (label, days)

    Event wateringEvent = Event(
      plantType: plantType, 
      eventType: EventType.watering
    );

    Event fertilizingEvent = Event(
      plantType: plantType, 
      eventType: EventType.fertilizing
    );

// Due to the need of converting db records to displayed values, there's a need of 
// using [FutureBuilder] - its results are stored as snapshot

    return FutureBuilder<List<EventInfo>>(

      // Functions that have to be done before build method is evoked
      future: Future.wait([
        getEventInfo(wateringEvent, 'Watering'),
        getEventInfo(fertilizingEvent, 'Fertilizing'),
      ]),

      // Build method
      builder: (context, snapshot) {

        // Async snapshot handler
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const CupertinoActivityIndicator();
        } else if (snapshot.hasError) {
          return Text('Error: ${snapshot.error}');

        // Build method if the snapshot is correct 
        } else if (snapshot.hasData) {
          EventInfo wateringInfo = snapshot.data![0];
          EventInfo fertilizingInfo = snapshot.data![1];

          return CupertinoListTile.notched(
            title: Text(plantType.name),
            subtitle: FrequenciesInfoDisplay([
              wateringInfo.description, 
              fertilizingInfo.description
            ])
          );

        // Null-safety check
        } else {
          return noRecordsDisplay;
        }
      },
    );
  }
}

// =======================================================================

// Widget used to display descriptions (excluded to keep the code clean); it has
// a positional parameter, that's a list of <Strings>

class FrequenciesInfoDisplay extends StatelessWidget {
  final List<String> descriptions;

  const FrequenciesInfoDisplay(this.descriptions, {super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: descriptions

      // Each child (labeled as 'description') is wrapped inside [Padding]
      // to ensure the right amount of space 

          .map((description) => Padding(
                padding: const EdgeInsets.only(bottom: 5),
                child: Text(description),
              ))
          .toList(),
    );
  }
}