Dimibe / sticky_grouped_list

A ScrollablePositionedList in which items can be grouped into sections with sticky headers.
https://pub.dev/packages/sticky_grouped_list
MIT License
184 stars 70 forks source link

Null check operator used on a null value #71

Open pimvanderheijden opened 3 months ago

pimvanderheijden commented 3 months ago

Describe the bug I get

Null check operator used on a null value

From this piece of code (sticky_grouped_list.dart line 407)

@override
  void jumpTo({
    required int index,
    double alignment = 0,
    bool automaticAlignment = true,
  }) {
    if (automaticAlignment) {
      alignment = _stickyGroupedListViewState!.headerDimension ?? alignment;
    }
    return super.jumpTo(index: index * 2 + 1, alignment: alignment);
  }

Apparently _stickyGroupedListViewState is not attached and I can't find out why.

Expected behavior StickyGroupedListViewState to assert or throw with a clear and concise message describing the issue.

Information:

Dimibe commented 3 months ago

Hi @pimvanderheijden, can you please provide more details on how you use the widget, e.g. the part of code where you define the widget?

pimvanderheijden commented 3 months ago

Well the Null check operator used on a null value occurs when you time things badly, like this:

// ignore: file_names
import 'package:flutter/material.dart';
import 'package:logger/logger.dart';
import 'package:visibility_detector/visibility_detector.dart';
import 'package:sticky_grouped_list/sticky_grouped_list.dart';
import 'package:watch_it/watch_it.dart';

class TestObject {
  final int index;
  final String name;
  final DateTime timeStart;

  TestObject({
    required this.index,
    required this.name,
    required this.timeStart,
  });
}

class EventsList extends StatefulWidget with WatchItStatefulWidgetMixin {
  const EventsList({
    super.key,
  });

  @override
  State<EventsList> createState() => _EventsListState();
}

// This is not EventsListState from state/events_list_state.dart
class _EventsListState extends State<EventsList> {
  final GroupedItemScrollController _itemScrollController = GroupedItemScrollController();
  Logger logger = GetIt.I.get<Logger>();

  @override
  Widget build(BuildContext context) {
    // here!
    _itemScrollController.jumpTo(index: 0);

    return StickyGroupedListView<TestObject, DateTime>(
      elements: [
        TestObject(index: 0, name: '0', timeStart: DateTime.now()),
        TestObject(index: 1, name: '1', timeStart: DateTime.now()),
      ],
      initialScrollIndex: 0,
      itemScrollController: _itemScrollController,
      groupBy: (element) => DateTime(element.timeStart.year, element.timeStart.month),
      groupSeparatorBuilder: (element) {
        return Padding(
          padding: const EdgeInsets.all(5.0),
          child: Text(
            element.timeStart.toString(),
            textAlign: TextAlign.start,
          ),
        );
      },
    );
  }
}

You have to add guards like _itemScrollController.isAttached and schedule jumpTo to trigger after the initial build call, for example with Future.delayed(Duration.zero, () {}).

This is not clear from the beginning. A nice error message