build method

  1. @override
Widget build(
  1. BuildContext context
)
override

Implementation

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      leading: widget.appBarLeading == null
          ? null
          : widget.appBarLeading!(context),
      leadingWidth: widget.leadingWidth,
      title: _getScaffoldTitle(context),
      actions: <Widget>[
        /// Select All Button
        if (widget.selection &&
            widget.multipleSelection &&
            widget.invertSelection)
          IconButton(
            tooltip: widget.invertSelectionText,
            icon: const Icon(Icons.select_all),
            onPressed: () {
              for (final T model in _globalItems) {
                if (_selections.containsKey(model.id)) {
                  _selections.remove(model.id);
                } else {
                  _selections[model.id!] = model;
                }
              }
              _streamController.add(AbstractListStateEnum.finishLoading);
            },
          ),

        /// Search Button
        if (widget.showSearchButton)
          IconButton(
            tooltip: sprintf(
              widget.searchButtonText,
              <dynamic>[widget.builder.superSingle(context)],
            ),
            icon: const Icon(Icons.search),
            onPressed: _search,
          ),

        /// Refresh Button
        if (widget.showRefreshButton)
          IconButton(
            tooltip: widget.refreshButtonText,
            icon: const Icon(FontAwesomeIcons.arrowsRotate),
            onPressed: () => _loadData(context),
          ),

        /// Selection Confirm Button
        if (widget.selection && widget.multipleSelection)
          IconButton(
            tooltip: sprintf(
              widget.selectionText,
              <dynamic>[widget.builder.superPlural(context)],
            ),
            icon: const FaIcon(FontAwesomeIcons.check),
            onPressed: () =>
                Navigator.of(context).pop(List<T>.of(_selections.values)),
          ),

        /// Actions
        if (!widget.selection && widget.actions != null)
          ...widget.actions!(
            context,
            widget.builder,
            widget.consumer,
            _qsParam,
            selection: widget.selection,
          ),

        /// Add Button
        if (!FollyFields().isMobile && !widget.selection)
          ValueListenableBuilder<bool>(
            valueListenable: _insertNotifier,
            builder: (BuildContext context, bool insert, _) {
              return insert
                  ? IconButton(
                      tooltip: sprintf(
                        widget.addText,
                        <dynamic>[widget.builder.superSingle(context)],
                      ),
                      icon: const FaIcon(FontAwesomeIcons.plus),
                      onPressed: _addEntity,
                    )
                  : const SizedBox.shrink();
            },
          ),

        /// Legend Button
        if (!widget.selection &&
            widget.builder.listLegend(context).isNotEmpty)
          IconButton(
            tooltip: widget.builder.listLegendTitle(context),
            icon: FaIcon(widget.builder.listLegendIcon(context)),
            onPressed: _showListLegend,
          ),
      ],
    ),
    floatingActionButton: FollyFields().isMobile && !widget.selection
        ? ValueListenableBuilder<bool>(
            valueListenable: _insertNotifier,
            builder: (BuildContext context, bool insert, _) {
              return insert
                  ? FloatingActionButton(
                      tooltip: sprintf(
                        widget.addText,
                        <dynamic>[widget.builder.superSingle(context)],
                      ),
                      onPressed: _addEntity,
                      child: const FaIcon(FontAwesomeIcons.plus),
                    )
                  : const SizedBox.shrink();
            },
          )
        : null,
    bottomNavigationBar: widget.builder.buildBottomNavigationBar(context),
    body: widget.builder.buildListBody(
      context,
      SafeFutureBuilder<bool>(
        future: _loadPermissions(context),
        waitingMessage: widget.waitingText,
        builder: (BuildContext context, bool value, _) {
          return SafeStreamBuilder<AbstractListStateEnum>(
            stream: _streamController.stream,
            waitingMessage: widget.waitingText,
            builder: (BuildContext context, AbstractListStateEnum event, _) {
              if (event == AbstractListStateEnum.loadingMessage) {
                return WaitingMessage(message: widget.waitingText);
              }

              /// CircularProgressIndicator will be at the list bottom,
              /// so we make space here with an extra index if
              /// event is incrementalLoading
              int itemCount = _globalItems.length;
              if (event == AbstractListStateEnum.incrementalLoading) {
                itemCount++;
              }

              /// If this is the first 'finishLoading' event and the
              /// scrollbar hasn't even appeared yet, we won't be able to
              /// scroll further.
              /// This callback will be called after building this widget.
              if (event == AbstractListStateEnum.finishLoading &&
                  !_initiallyFilled) {
                WidgetsBinding.instance.addPostFrameCallback(
                  (_) async {
                    if (_scrollController.positions.isNotEmpty &&
                        _scrollController.position.hasContentDimensions &&
                        _scrollController.position.maxScrollExtent == 0) {
                      int extraAmount =
                          await _loadData(context, clear: false);
                      if (extraAmount == 0) {
                        // This flags that we won't try further '_loadData'
                        // calls
                        _initiallyFilled = true;
                      }
                    }
                  },
                );
              }

              return RefreshIndicator(
                key: _refreshIndicatorKey,
                onRefresh: () => _loadData(context),
                child: _globalItems.isEmpty
                    ? TextMessage(
                        sprintf(
                          widget.listEmpty,
                          <dynamic>[
                            widget.builder.superPlural(context).toLowerCase(),
                          ],
                        ),
                      )
                    : KeyboardListener(
                        autofocus: true,
                        focusNode: keyboardFocusNode,
                        onKeyEvent: (KeyEvent event) {
                          if (widget.showSearchButton &&
                              event.character != null) {
                            _search(event.character);
                          }
                        },
                        child: Scrollbar(
                          controller: _scrollController,
                          // isAlwaysShown: FollyFields().isWeb,
                          thumbVisibility: true,
                          child: ListView.separated(
                            physics: const AlwaysScrollableScrollPhysics(),
                            padding: const EdgeInsets.all(16),
                            controller: _scrollController,
                            itemBuilder: (BuildContext context, int index) {
                              /// Updating...
                              if (index >= _globalItems.length) {
                                return const SizedBox(
                                  height: 80,
                                  child: Center(
                                    child: CircularProgressIndicator(),
                                  ),
                                );
                              }

                              T model = _globalItems[index];

                              return _delete &&
                                      FollyFields().isMobile &&
                                      widget.canDelete(model)
                                  ? Dismissible(
                                      key: Key('key_${model.id}'),
                                      direction: DismissDirection.endToStart,
                                      background: Container(
                                        color: Colors.red,
                                        alignment: Alignment.centerRight,
                                        padding: const EdgeInsets.only(
                                          right: 16,
                                        ),
                                        child: const FaIcon(
                                          FontAwesomeIcons.trashCan,
                                          color: Colors.white,
                                        ),
                                      ),
                                      confirmDismiss:
                                          (DismissDirection direction) =>
                                              _askDelete(),
                                      onDismissed:
                                          (DismissDirection direction) =>
                                              _deleteEntity(model),
                                      child: _buildResultItem(
                                        model: model,
                                        selected:
                                            _selections.containsKey(model.id),
                                        canDelete: false,
                                      ),
                                    )
                                  : _buildResultItem(
                                      model: model,
                                      selected:
                                          _selections.containsKey(model.id),
                                      canDelete: _delete &&
                                          FollyFields().isNotMobile &&
                                          widget.canDelete(model),
                                    );
                            },
                            separatorBuilder: (_, __) => const FollyDivider(),
                            itemCount: itemCount,
                          ),
                        ),
                      ),
              );
            },
          );
        },
      ),
    ),
  );
}