Flutter -如何在没有构建方法的类中获取上下文

llycmphe  于 2022-12-05  发布在  Flutter
关注(0)|答案(2)|浏览(194)

我正在为PaginatedDataTable编写代码,但在尝试使用showDialog()时我迷失了方向。我想要的是,当用户按下DataTable中的一行时,会出现一个对话框,其中包含该行所包含的信息。我遇到的问题是对话框的上下文,因为它需要上下文,而DataRow类本身并没有上下文。
为了识别已选择的行,必须在DataRow类中使用onSelectChanged()。在这里,我试图调用对话框,但必须使用全局导航键。

class CalendarResultsDataSource extends DataTableSource {
  final List<CalendarResult> _calendarResults;
  CalendarResultsDataSource(this._calendarResults);

  @override
  DataRow getRow(int index) {
    assert(index >= 0);
    if (index >= _calendarResults.length) return null;
    final CalendarResult calendarResult = _calendarResults[index];
    return DataRow.byIndex(
        index: index,
        selected: calendarResult.selected,
        onSelectChanged: (bool value) {
          print(value);
          print(calendarResult.agency);
          Dialogs.showAuditInfo(
              navigatorKey.currentState.overlay.context, calendarResult);  //<-- this feels hacky
        },
        cells: <DataCell>[
          DataCell(Container(
              height: double.infinity,
              width: double.infinity,
              color: index.isEven
                  ? ColorDefs.colorAlternatingDark
                  : ColorDefs.colorDarkBackground,
              child: Padding(
                padding: const EdgeInsets.fromLTRB(6.0, 4.0, 4.0, 4.0),
                child: Center(
                    child: Text('${calendarResult.getDateFormatted()}',
                        style: ColorDefs.textBodyWhite15)),
              ))), etc. etc.

        ]);

在dataRow类别中。
因此,我在main()中创建了一个导航键,如下所示:

final navigatorKey = GlobalKey<NavigatorState>();

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: navigatorKey,
      home: Scaffold(body: AwesomeAppCodeHere())
    );
  }
}

然后,我在onSelectChanged()方法中引用该键,如下所示:

showDialog<void>(
      context: navigatorKey.currentState.overlay.context,
      builder: (BuildContext context) {
        return AlertDialog(
          content: StatefulBuilder(
            builder: (BuildContext context, StateSetter setState) {
              return DialogInnards();
            },
          ),
        );
      },
    );

我能想到的唯一解决方案是将上下文从PaginatedDataTable小部件传递给DataRow类,但这样我就必须直接修改flutter DataRow小部件。
那么,在没有公开上下文的情况下,在类中获取上下文的最佳方法是什么呢?
我确实看到了“get”包:也许可以...... https://pub.dev/packages/get,但对于一些简单明了的东西来说,这不是矫枉过正吗?(但只是逃避我?)

hi3rlvi2

hi3rlvi21#

我正在处理同样的事情,并且我找到了一个解决方法。您可以在DataTableSource实现类中声明一个BuildContext属性。

class EventData extends DataTableSource {
      final BuildContext context;
      EventData({required this.context});
      final List<Map<String, dynamic>> _data = List.generate(
          200,
          (index) => {
                "price": Random().nextInt(1000),
              });
    
      @override
      DataRow? getRow(int index) {
        return DataRow(cells: [
          DataCell(Center(
            // We can know use the context in this method.
            child: Text("${_data[index]['price']} €",
                style: Theme.of(context)
                    .textTheme
                    .headline6!
                    .copyWith(fontWeight: FontWeight.bold)),
          )),
        ]);
      }
    
      @override
      bool get isRowCountApproximate => false;
    
      @override
      int get rowCount => _data.length;
    
      @override
      int get selectedRowCount => 0;
    }

此属性必须由UI提供,现在可以在DataTableSource类的方法中使用。

class PaginatedExample extends StatelessWidget {
  const PaginatedExample({super.key});

  @override
  Widget build(BuildContext context) {
    return PaginatedDataTable(
      source: EventData(context: context), //The context can be provided here.
      columns: const [
        DataColumn(label: Text('Price')),
      ],
    );
  }
}

此解决方案的缺点是EventData类将在每次UI重建时重新示例化。

6fe3ivhb

6fe3ivhb2#

正如文档中所述,DataTableSource是长期存在的对象,应该在PaginatedDataTable的构建中重用,因此您不应该将上下文传递给DataTableSource对象IMO。
我更喜欢传递回调函数,它将在点击行后被调用。
以下是我在应用程序中使用的内容的简化示例:

class PlayerDataSource extends DataTableSource {

  /// When no other user is selected, this callback will be called from the onTap event on a Datarow.
  final void Function(Player player)? onTap;

  /// Allow the pagined data table to know if it should show the checkbox
  final void Function(bool displayCheckbox)? onSelect;

  PlayerDataSource({this.onTap, this.onSelect});

  List<Player> displayedPlayers = [];
  int _selectedCount = 0;

  @override
  int get rowCount => displayedPlayers.length;

  @override
  bool get isRowCountApproximate => false;

  @override
  int get selectedRowCount => _selectedCount;

  @override
  DataRow? getRow(int index) {
    assert(index >= 0);
    if (index >= displayedPlayers.length) {
      return null;
    }
    final player = displayedPlayers[index];
    return DataRow.byIndex(
      index: index,
      selected: player.selected,
      onSelectChanged: (isSelected) {
        if (_selectedCount == 0) {
          onTap?.call(player);
          return;
        }
        if (_selectedCount == 1 && isSelected != null && !isSelected) {
          onSelect?.call(false);
        }
        selectOne(player, isSelected ?? false);
      },
      onLongPress: _selectedCount == 0
          ? () {
              onSelect?.call(true);
              selectOne(player, true);
            }
          : null,
      cells: [
        // Wrap Text with builder to access context and get localizations
        DataCell(Builder(builder: (context) {
          final local = AppLocalizations.of(context)!;
          return Text(player.hasSub ? local.yes : local.no);
        })),
        DataCell(Text('0')),
      ],
    );
  }
}

PS:如果需要单元格内的上下文,可以将单元格子部件(这里是Text部件) Package 在Builder部件内

相关问题