skip to Main Content

I want to sort a list of items and to add a custom title/header on top of some items from the list like in the photo attached below:
enter image description here

What I try to achieve is that if the first element from the list have the property "isHooked = true" then I want to add on top of first row a header called "Current Trailer" and on top of second row I want to add a header called "Past Trailers" which will include the other items from the list.

Can you help me to understand what I’m doing wrong in my code ?

import 'package:flutter/material.dart';

const Color darkBlue = Color.fromARGB(255, 18, 32, 47);

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: darkBlue,
      ),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: MyWidget(),
        ),
      ),
    );
  }
}

class Trailer {
  Trailer({this.trailerPlate, this.isHookup, this.time});

  String? trailerPlate = "";
  bool? isHookup = false;
  String? time = "";
}

List<Trailer> trailersHistory = [
  Trailer(trailerPlate: "Trailer4", isHookup: true, time: "16:00"),
  Trailer(trailerPlate: "Trailer3", isHookup: false, time: "15:00"),
  Trailer(trailerPlate: "Trailer2", isHookup: false, time: "14:00"),
  Trailer(trailerPlate: "Trailer1", isHookup: false, time: "13:00"),
];

class MyWidget extends StatelessWidget {
  Widget customCard({required Widget widget, required Color cardColor}) {
    return Card(
      color: cardColor,
      child: Padding(padding: const EdgeInsets.all(8.0), child: widget),
    );
  }

  Widget displayTrailersHistoryTable() {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        (trailersHistory.first.isHookup ?? false) == true
            ? const Text("Current Trailer",
                style: TextStyle(color: Colors.green))
            : const Text("Past Trailers", style: TextStyle(color: Colors.red)),
        Expanded(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: ListView.builder(
              scrollDirection: Axis.vertical,
              physics: const BouncingScrollPhysics(
                parent: AlwaysScrollableScrollPhysics(),
              ),
              itemCount: trailersHistory.length,
              itemBuilder: (BuildContext context, int index) {
                Trailer currentTrailer = trailersHistory[index];
                return customCard(
                    widget: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          Text(currentTrailer.trailerPlate ?? ""),
                          (currentTrailer.isHookup ?? false)
                              ? const Icon(
                                  Icons.add_circle,
                                  color: Colors.green,
                                  size: 30.0,
                                )
                              : const Icon(
                                  Icons.remove_circle,
                                  color: Colors.red,
                                  size: 30.0,
                                ),
                          Text("@ ${currentTrailer.time}")
                        ]),
                    cardColor: Colors.grey);
              },
            ),
          ),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      alignment: FractionalOffset.center,
      child: displayTrailersHistoryTable(),
    );
  }
}

Here is also a link to my small demo:
Dart Pad Link for Demo

2

Answers


  1. Chosen as BEST ANSWER

    Thanks to @AtMaintx I managed to implement the solution. Here is the full code:

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          theme: ThemeData.dark().copyWith(
            scaffoldBackgroundColor: const Color.fromARGB(255, 18, 32, 47),
          ),
          debugShowCheckedModeBanner: false,
          home: Scaffold(
            body: Center(
              child: MyWidget(),
            ),
          ),
        );
      }
    }
    
    class Trailer {
      Trailer({this.trailerPlate, this.isHookup, this.time});
    
      String? trailerPlate = "";
      bool? isHookup = false;
      String? time = "";
    }
    
    List<Trailer> trailersHistory = [
      Trailer(trailerPlate: "Trailer4", isHookup: true, time: "16:00"),
      Trailer(trailerPlate: "Trailer3", isHookup: false, time: "15:00"),
      Trailer(trailerPlate: "Trailer2", isHookup: false, time: "14:00"),
      Trailer(trailerPlate: "Trailer1", isHookup: false, time: "13:00"),
    ];
    
    class MyWidget extends StatelessWidget {
      Widget customCard({required Widget widget, required Color cardColor}) {
        return Card(
          color: cardColor,
          child: Padding(padding: const EdgeInsets.all(8.0), child: widget),
        );
      }
    
      Widget displayTrailersHistoryTable() {
        bool hookedCurrentTrailer = (trailersHistory.first.isHookup ?? false);
        return Padding(
          padding: const EdgeInsets.all(8.0),
          child: ListView.builder(
            scrollDirection: Axis.vertical,
            physics: const BouncingScrollPhysics(
              parent: AlwaysScrollableScrollPhysics(),
            ),
            itemCount: hookedCurrentTrailer
                ? trailersHistory.length + 2
                : trailersHistory.length + 1,
            itemBuilder: (BuildContext context, int index) {
              int newIndex = index;
              if (index == 0) {
                return hookedCurrentTrailer
                    ? const Text("Current Trailer",
                        style: TextStyle(color: Colors.green))
                    : const Text("Past Trailers",
                        style: TextStyle(color: Colors.red));
              }
              if (hookedCurrentTrailer) {
                if (index == 2) {
                  return const Text("Past Trailers",
                      style: TextStyle(color: Colors.red));
                }
                if (index > 2) {
                  newIndex--;
                }
              }
              Trailer currentTrailer = trailersHistory[newIndex - 1];
              return Column(
                children: [
                  customCard(
                      widget: Row(
                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                          children: [
                            Text(currentTrailer.trailerPlate ?? ""),
                            (currentTrailer.isHookup ?? false)
                                ? const Icon(
                                    Icons.add_circle,
                                    color: Colors.green,
                                    size: 30.0,
                                  )
                                : const Icon(
                                    Icons.remove_circle,
                                    color: Colors.red,
                                    size: 30.0,
                                  ),
                            Text("@ ${currentTrailer.time}")
                          ]),
                      cardColor: Colors.grey),
                ],
              );
            },
          ),
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return Container(
          color: Colors.white,
          alignment: FractionalOffset.center,
          child: displayTrailersHistoryTable(),
        );
      }
    }
    

  2. Seem like you want "Past Trailers" to be between the items made by the itemBuilder. Here is a possible solution.

    At the start of the function displayTrailersHistoryTable put:bool hasCurrentTrailer = (trailersHistory.first.isHookup ?? false);. Then make itemCount into: itemCount: hasCurrentTrailer ? trailersHistory.length+1 : trailersHistory.length,. At the start of the item builder put: int newIndex = index;. Then next line is: if(hasCurrentTrailer) { if(index == 1) return const Text("Past Trailers", style: TextStyle(color: Colors.red)); if(index > 1) newIndex--; }. Now you just have to get the trailer from trailer history using newIndex instead of index. With these changes you will get "Past Trailers" in between the first and second item in the case that the first item isHookup equals true.

    — Update, steps to make top text scrollable —

    Remove the first child from the columns children as we move it to the itemBuilder. Set itemCount to itemCount: hasCurrentTrailer ? trailersHistory.length+2 : trailersHistory.length+1,.

    The first line in itemBuilder will be: if(index == 0) return hasCurrentTrailer ? const Text("Current Trailer", style: TextStyle(color: Colors.green)) : const Text("Past Trailers", style: TextStyle(color: Colors.red));.

    Update the line before currentTrailer to: if(hasCurrentTrailer) { if(index == 2) return const Text("Past Trailers", style: TextStyle(color: Colors.red)); if(index > 2) newIndex--; }.

    Finally change trailersHistory[newIndex] to trailersHistory[newIndex-1].

    Now the top text is the first item in the list and is now scrollable.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search