Open abdulrehmananwar opened 3 weeks ago
Hi @abdulrehmananwar ,
Based on the details provided, in the buildEditWidget, you have returned null for the Quantity2 column. The buildEditWidget is used to obtain the TextField widget when a cell is moved into edit mode. Therefore, while the cell was intended to be moved into edit mode, you have restricted the addition of the TextField widget by returning null. As a result, the cells in the Quantity2 column did not enter edit mode, even though focus switched to it. We believe that the cells in the Quantity2 column not entering edit mode is the issue you encountered.
To better assist you with your query, could you clarify why you are restricting the addition of the TextField widget for the cells in the Quantity2 column when focus switches?. Additionally, please provide a video recording demonstrating the issue for better clarity. Please provide relevant details that may help us better understand your request.
Regards, Abinesh P
I am currently facing three major issues with focus management when handling the CanCellSubmit event, particularly when opening a dialog to filter search results based on user input.
TextField Not Focusable: When the dialog opens, the TextField within it is not receiving focus. Even when clicked, it does not gain focus, which prevents the user from entering input.
Dialog Reopening Upon Dismissal: If the dialog is dismissed without selecting any value, it unexpectedly reopens immediately. On the second dismissal, it closes properly. The dialog should only reopen if the user presses the Tab key or clicks elsewhere, not automatically upon dismissal.
Focus Management in Price Column: I have a price column where user input is not required, so I do not return a TextField in the widget builder. However, this causes issues with focus traversal when using the Tab key, as the focus does not move as expected.
i have shared you sample Code and video please respond on it Thanks.
https://github.com/user-attachments/assets/0f5c454f-480d-4a2c-a059-c5451f4b018f GridIssues.zip
Hi @abdulrehmananwar
Query | Response |
---|---|
Dialog Reopening Upon Dismissal: If the dialog is dismissed without selecting any value, it unexpectedly reopens immediately. On the second dismissal, it closes properly. The dialog should only reopen if the user presses the Tab key or clicks elsewhere, not automatically upon dismissal. | I have adjusted the sample you provided, and we have resolved the issue with opening and closing the dialog box. In the handleKeyEvent method of the RowSelectionManager, we need to set the dialog box flag variable to true after handling the key event and then to false when appropriate. This will rectify the issue with the dialog box opening and closing. |
Focus Management in Price Column: I have a price column where user input is not required, so I do not return a TextField in the widget builder. However, this causes issues with focus traversal when using the Tab key, as the focus does not move as expected. | After rectifying the issue, the dialog box now opens and closes properly. We are unable to reproduce the focus traversal issue when using the Tab key on our side. Based on the video reference, we attempted to replicate the issue, but it seems to have been resolved. |
TextField Not Focusable: When the dialog opens, the TextField within it is not receiving focus. Even when clicked, it does not gain focus, which prevents the user from entering input. | Based on the details provided, you are triggering the showDialog in the canSubmitCell method. The DataGridSource.canSubmitCell is called before the cell finishes editing. If you want to prevent the cell from ending its editing state, you can return false. In this scenario, the DataGrid cell remains in edit mode with the TextField active, so the focus is maintained solely on the DataGrid. When the dialog box opens, the focus stays on the DataGrid because the cell is still in edit mode. Instead of triggering the dialog box in canSubmitCell, you can directly invoke showDialog in the buildEditWidget method for the specific cell based on the columnName, rather than loading the TextField widget. |
If there are any misunderstandings regarding your requirements, or if your needs differ, please provide specific and clear details about why you are using showDialog in canSubmitCell. What is the core reason for including the dialog? This additional information will help us thoroughly address your request and provide an appropriate solution.
We have included a modified sample for your reference. Please review it for
further details
Regards,
Abinesh P
Bug description
i have write a logic on buildEditWidget event after that focus travel on tab key not working properly . my code is as below. if (column.columnName == 'Quantity2') { return null; }
Steps to reproduce
@override Widget? buildEditWidget(DataGridRow dataGridRow, RowColumnIndex rowColumnIndex, GridColumn column, CellSubmit submitCell) { // Check if the column is 'Quantity2' and return null if so if (column.columnName == 'Quantity2') { return null; }
}
Code sample
import 'package:flutter/material.dart'; import 'package:syncfusion_flutter_datagrid/datagrid.dart'; import 'package:flutter/services.dart'; import 'package:collection/collection.dart';
class CustomSelectionManager extends RowSelectionManager { CustomSelectionManager({ required this.isDialogOpen, required this.dataGridController, required this.dataGridSource, required this.context, required this.lastColumnIndex, });
final bool isDialogOpen; final DataGridController dataGridController; final DataGridSource dataGridSource; final int lastColumnIndex; final BuildContext context;
@override Future handleKeyEvent(KeyEvent keyEvent) async {
if (keyEvent.logicalKey == LogicalKeyboardKey.tab ||
keyEvent.logicalKey == LogicalKeyboardKey.enter) {
if (!isDialogOpen) {
await super.handleKeyEvent(keyEvent).then(
(value) =>
WidgetsBinding.instance.addPostFrameCallback((_) async {
await dataGridController
.beginEdit(dataGridController.currentCell);
}),
);
}
}
}
}
class GridEntryDataSource extends DataGridSource { GridEntryDataSource(this.dataGridController, this.fieldNames) { _initializeDataList(); updateDataGridRows(); }
DataGridController dataGridController; final List fieldNames;
List dataGridRows = [];
List<Map<String, String>> dataList = [];
// Change _isPasting to ValueNotifier
final ValueNotifier _isPasting = ValueNotifier(false);
dynamic newCellValue;
void _initializeDataList() { dataList.add({for (var v in fieldNames) v: ''}); }
@override List get rows => dataGridRows;
void updateDataGridRows() { dataGridRows = dataList.map((data) => _createDataRow(data)).toList();
notifyListeners();
}
DataGridRow _createDataRow(Map<String, String> data) { return DataGridRow( cells: fieldNames .map((field) => DataGridCell(columnName: field, value: data[field]))
.toList(),
);
}
Future handlePasteAndNavigate(
int startRowIndex, int startColumnIndex) async {
if (_isPasting.value) return;
_isPasting.value = true;
final clipboardData = await Clipboard.getData('text/plain');
}
void resetGrid() { dataList.clear(); _initializeDataList(); updateDataGridRows(); }
Future _onCellSubmitted(
DataGridRow row, String columnName, String value) async {
final rowIndex = dataGridController.currentCell.rowIndex;
}
@override Future onCellSubmit(DataGridRow dataGridRow,
RowColumnIndex rowColumnIndex, GridColumn column) async {
// Get the current row index from the data grid controller.
final currentRowIndex = dataGridController.currentCell.rowIndex;
}
@override Future canSubmitCell(DataGridRow dataGridRow,
RowColumnIndex rowColumnIndex, GridColumn column) async {
return Future.value(true);
}
@override DataGridRowAdapter buildRow(DataGridRow row) { return DataGridRowAdapter( cells: row.getCells().map((cell) {
return Container(
alignment: Alignment.center,
child: Text(cell.value?.toString() ?? ''),
);
}).toList(),
);
}
@override Widget? buildEditWidget(DataGridRow dataGridRow, RowColumnIndex rowColumnIndex, GridColumn column, CellSubmit submitCell) { // Check if the column is 'Quantity2' and return null if so if (column.columnName == 'Quantity2') { return null; }
}
}
class DataGridExample extends StatefulWidget { const DataGridExample({super.key});
@override // ignore: library_private_types_in_public_api _DataGridExampleState createState() => _DataGridExampleState(); }
class _DataGridExampleState extends State {
List fieldNames = ['ItemName', 'Quantity', 'Quantity2', 'Price', 'Amount'];
late DataGridController dataGridController;
late GridEntryDataSource dataSource;
String selectedOption = 'AgeFields';
@override void initState() { super.initState(); dataGridController = DataGridController(); dataSource = GridEntryDataSource(dataGridController, fieldNames); }
void changeColumns(String option) { setState(() { selectedOption = option; fieldNames.clear(); dataGridController = DataGridController(); dataSource = GridEntryDataSource(dataGridController, fieldNames); }); }
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Editable DataGrid Example'), actions: [ ValueListenableBuilder(
valueListenable: dataSource._isPasting,
builder: (context, isPasting, child) {
return IgnorePointer(
ignoring: isPasting,
child: IconButton(
icon: const Icon(Icons.paste),
onPressed: () async {
final focusedCell = dataSource.rows.first;
int rowIndex = dataSource.rows.indexOf(focusedCell);
await dataSource.handlePasteAndNavigate(rowIndex, 0);
},
),
);
},
),
ValueListenableBuilder(
valueListenable: dataSource.isPasting,
builder: (context, isPasting, child) {
return IgnorePointer(
ignoring: isPasting,
child: IconButton(
icon: const Icon(Icons.refresh),
onPressed: () {
dataGridController.endEdit();
WidgetsBinding.instance.addPostFrameCallback(() {
dataSource.resetGrid();
});
},
),
);
},
),
],
),
body: Column(
children: [
Expanded(
child: SfDataGrid(
allowColumnsResizing: true,
selectionMode: SelectionMode.single,
navigationMode: GridNavigationMode.cell,
editingGestureType: EditingGestureType.tap,
allowEditing: true,
source: dataSource,
controller: dataGridController,
selectionManager: CustomSelectionManager(
isDialogOpen: false,
dataGridController: dataGridController,
dataGridSource: dataSource,
context: context,
lastColumnIndex: fieldNames.length - 1,
),
columns: fieldNames.map((fieldName) {
return GridColumn(
columnName: fieldName,
label: Container(
alignment: Alignment.center,
child: Text(fieldName,
style: const TextStyle(fontWeight: FontWeight.bold)),
),
);
}).toList(),
),
),
],
),
);
}
}
void main() { runApp(const MaterialApp( home: DataGridExample(), debugShowCheckedModeBanner: false, )); }
Screenshots or Video
Screenshots / Video demonstration
[Upload media here]Stack Traces
Stack Traces
```dart [Add the Stack Traces here] ```On which target platforms have you observed this bug?
Windows
Flutter Doctor output
Doctor summary (to see all details, run flutter doctor -v): [√] Flutter (Channel stable, 3.24.4, on Microsoft Windows [Version 10.0.19045.5073], locale en-US) [√] Windows Version (Installed version of Windows is version 10 or higher) [√] Android toolchain - develop for Android devices (Android SDK version 35.0.0) [√] Chrome - develop for the web [√] Visual Studio - develop Windows apps (Visual Studio Professional 2022 17.10.4) [√] Android Studio (version 2024.2) [√] Connected device (3 available) [√] Network resources