Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update DataCell once it loaded when using AsyncDataTableSource #299

Open
devrndsvits opened this issue Aug 28, 2024 · 2 comments
Open

Update DataCell once it loaded when using AsyncDataTableSource #299

devrndsvits opened this issue Aug 28, 2024 · 2 comments

Comments

@devrndsvits
Copy link

Thanks a lot for amazing library @maxim-saplin

I'm using AsyncDataTableSource with dummy api and it works perfectly fine but I have requirement when user select a row I want to show icon on first column which is fixed column. Icon represent the indication that row is selected.

I'm not able find any event that update cell or refresh the cell when row is tap. Below is minimal production code thus you can have a look it. I tried notifyListeners but still no luck.

Any suggestion and help is highly appreciated.

UserAsyncScreen

import 'package:data_table_2/data_table_2.dart';
import 'package:datatable_paging/user_model/user.dart';
import 'package:datatable_paging/using_lib/user_async_data_source.dart';
import 'package:flutter/material.dart';

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

  @override
  State<UserAsyncScreen> createState() => _UserAsyncScreenState();
}

class _UserAsyncScreenState extends State<UserAsyncScreen> {
  int _rowsPerPage = PaginatedDataTable.defaultRowsPerPage;
  bool _sortAscending = true;
  int? _sortColumnIndex;
  UserAsyncDataSource? _userAsyncDataSource;
  final PaginatorController _controller = PaginatorController();

  bool _dataSourceLoading = false;
  int _initialRow = 0;

  bool isColumnAutoWidth = true;

  User? _selectedUser;
  @override
  void initState() {
    _userAsyncDataSource = UserAsyncDataSource(
      isColumnAutoWidth: isColumnAutoWidth,
      onRowSelected: (User selectedUser) {
        setState(() {
          _selectedUser = selectedUser; // Update selected user
          print(_selectedUser?.firstname.toString() ?? "");
        });
      },
    );
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    if (_dataSourceLoading) return const SizedBox();

    return Scaffold(
      appBar: AppBar(
        title: const Text('Data Table'),
      ),
      body: Column(
        children: [
          Expanded(
            child: AsyncPaginatedDataTable2(
                headingRowColor: WidgetStateColor.resolveWith(
                    (states) => Colors.purple[900]!),
                headingTextStyle: const TextStyle(color: Colors.white),
                horizontalMargin: 20,
                checkboxHorizontalMargin: 12,
                columnSpacing: 20,
                wrapInCard: false,
                rowsPerPage: _rowsPerPage,
                autoRowsToHeight: false,
                pageSyncApproach: PageSyncApproach.doNothing,
                showCheckboxColumn: false,
                minWidth:
                    isColumnAutoWidth ? MediaQuery.sizeOf(context).width : 2500,
                fit: FlexFit.tight,
                border: TableBorder(
                    top: const BorderSide(color: Colors.black),
                    bottom: BorderSide(color: Colors.grey[300]!),
                    left: BorderSide(color: Colors.grey[300]!),
                    right: BorderSide(color: Colors.grey[300]!),
                    verticalInside: BorderSide(color: Colors.grey[300]!),
                    horizontalInside:
                        const BorderSide(color: Colors.grey, width: 1)),
                availableRowsPerPage: const [5, 10, 15, 20],
                onRowsPerPageChanged: (value) {
                  _rowsPerPage = value!;
                },
                initialFirstRowIndex: _initialRow,
                onPageChanged: (rowIndex) {},
                sortColumnIndex: _sortColumnIndex,
                sortAscending: _sortAscending,
                sortArrowIcon: Icons.keyboard_arrow_up,
                sortArrowAnimationDuration: const Duration(milliseconds: 500),
                sortArrowBuilder: (ascending, sorted) {
                  if (sorted) {
                    if (ascending) {
                      return const Padding(
                        padding: EdgeInsets.only(left: 20.0),
                        child: Icon(
                          Icons.keyboard_arrow_down,
                          color: Colors.white,
                        ),
                      );
                    } else {
                      return const Padding(
                        padding: EdgeInsets.only(left: 20.0),
                        child: Icon(
                          Icons.keyboard_arrow_up,
                          color: Colors.white,
                        ),
                      );
                    }
                  } else {
                    return const SizedBox();
                  }
                },
                controller: _controller,
                hidePaginator: false,
                fixedLeftColumns: isColumnAutoWidth ? 1 : 1,
                fixedColumnsColor: Colors.purple[900],
                columns: [
                  const DataColumn2(label: Text(' '), fixedWidth: 30.0),
                  DataColumn2(
                    label: const Text('Id'),
                    onSort: (columnIndex, ascending) =>
                        sort(columnIndex, ascending),
                  ),
                  DataColumn2(
                    label: const Text('Firstname'),
                    onSort: (columnIndex, ascending) =>
                        sort(columnIndex, ascending),
                  ),
                  DataColumn2(
                    label: const Text('Lastname'),
                    onSort: (columnIndex, ascending) =>
                        sort(columnIndex, ascending),
                  ),
                ],
                empty: Center(
                  child: Container(
                    padding: const EdgeInsets.all(20),
                    color: Colors.grey[200],
                    child: const Text('No data'),
                  ),
                ),
                source: _userAsyncDataSource!),
          ),
        ],
      ),
    );
  }

  void sort(
    int columnIndex,
    bool ascending,
  ) {
    var columnName = "id";
    switch (columnIndex) {
      case 0:
        columnName = "id";
        break;
      case 1:
        columnName = "id";
        break;
      case 2:
        columnName = "Firstname";
        break;
      case 3:
        columnName = "Lastname";
        break;
    }
    _userAsyncDataSource!.sort(columnName, ascending);
    setState(() {
      _sortColumnIndex = columnIndex;
      _sortAscending = ascending;
    });
  }
}

UserAsyncDataSource

import 'package:data_table_2/data_table_2.dart';
import 'package:datatable_paging/api_manager/api_manager.dart';
import 'package:datatable_paging/user_model/user.dart';
import 'package:flutter/material.dart';

class UserAsyncDataSource extends AsyncDataTableSource {
  final APIManager apiManager = APIManager();

  final bool isColumnAutoWidth;
  final void Function(User)? onRowSelected;

  UserAsyncDataSource({this.isColumnAutoWidth = true, this.onRowSelected});

  String _sortColumn = "id";
  bool _sortAscending = true;

  User? selectedUser; // Currently selected user

  bool showSelection = false;

  var appColor = Colors.white;

  @override
  Future<AsyncRowsResponse> getRows(int startIndex, int count) async {
    int apiPage = ((startIndex / count) + 1).toInt();

    final List<User> newData = await apiManager.getUserDetailsPage(
      page: apiPage,
      limit: count,
      sortBy: _sortColumn,
      order: _sortAscending ? "asc" : "desc",
    );

    var r = AsyncRowsResponse(
        100,
        newData.map((user) {
          int index = newData.indexOf(user);
          return DataRow2(
            color: WidgetStateProperty.resolveWith<Color>((states) {
              if (states.contains(WidgetState.selected)) {
                return Colors.purple[50]!;
              }
              return Colors.transparent;
            }),
            key: ValueKey<int>(index),
            onSelectChanged: (selected) {
              if (selected != null) {
                appColor = Colors.amber;
                deselectAll();
                setRowSelection(ValueKey<int>(index), selected);
                onRowSelected?.call(user); // Notify when a row is selected
                selectedUser = user;
                notifyListeners();
              }
            },
            cells: [
              DataCell(
                Transform.translate(
                  offset: const Offset(-8.0, 0),
                  child: Icon(
                    Icons.play_arrow_rounded,
                    color: selectedUser == user
                        ? Colors.white
                        : Colors.transparent,
                  ),
                ),
              ),
              DataCell(Text(user.id)),
              DataCell(Text(user.firstname)),
              DataCell(Text(user.lastname)),
            ],
          );
        }).toList());

    return r;
  }

  Future<int> getTotalRecords() {
    return Future<int>.delayed(const Duration(milliseconds: 0), () => 100);
  }

  void sort(String columnName, bool ascending) {
    deselectAll();
    selectedUser = null;
    _sortColumn = columnName;
    _sortAscending = ascending;
    refreshDatasource();
  }

  @override
  void notifyListeners() {
    if (showSelection) {
      appColor = Colors.amber;
      showSelection = false;
    }
    super.notifyListeners();
  }
}

APIManager

import 'package:datatable_paging/user_model/user.dart';
import 'package:dio/dio.dart';

class APIManager {
  final Dio dioForUser = Dio();

  Future<List<User>> getUserDetailsPage(
      {required int page,
      required int limit,
      required String sortBy,
      required String order}) async {
    try {
      final response = await dioForUser.get(
        'https://66c6b5538b2c10445bc76efa.mockapi.io/api/v1/getUserDetails',
        queryParameters: {
          'page': page,
          'limit': limit,
          'sortBy': sortBy,
          'order': order
        },
      );
      if (response.statusCode == 200) {
        List<dynamic> data = response.data;
        return data.map((json) => User.fromJson(json)).toList();
      } else {
        throw Exception('Failed to load user details');
      }
    } catch (e) {
      throw Exception('Error fetching data: $e');
    }
  }
}
@maxim-saplin
Copy link
Owner

maxim-saplin commented Aug 28, 2024

@devrndsvits - if you would like to update the column title - the best you can do is refresh the whole widget. If you want to change the row - just update the datasource. Sorry, no capacity to look in your code...

@NourEldin-Ali
Copy link

Hi @maxim-saplin

Thank you for this plugin.

I have the same error for the notifyListeners in AsyncDataTableSource. I does not work well.

For example I added edit button at the end of row, and after click edit the Text the Text('test') should be modified to 'TextFormField(...)' but it does not changed (Note: I was tried before in other plugin it worked well, but it seems to me that the error in the notifyListeners).

Also, if i put in the DataRow( selected: true,...) the the checkbox is not selected.

@github-staff github-staff deleted a comment from ViniciusSCG Oct 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants
@maxim-saplin @NourEldin-Ali @devrndsvits and others