Flutter app state not mounted (setState) on click inside DataTable

354 views Asked by At

Whenever I click within a DataRow and try to setState(), apparently the main app state is not mounted. If anyone can point me into the right direction as to why this happens, I would greatly appreciate it. I really need to be able to update the app (setState) after clicking within a DataTable.

Example code:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  @override
  setState(fn){
    if (mounted) {
      super.setState(fn);
    } else {
      debugPrint('Not mounted');
    }
  }

  update() => setState((){});

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Demo'),
      ),
      body: PaginatedDataTable(
        showCheckboxColumn: true,
        columns: const <DataColumn>[
          DataColumn(
            label: Expanded(
              child: Text(
                'Name',
                style: TextStyle(fontStyle: FontStyle.italic),
              ),
            ),
          ),
          DataColumn(
            label: Expanded(
              child: Text(
                'Age',
                style: TextStyle(fontStyle: FontStyle.italic),
              ),
            ),
          ),
          DataColumn(
            label: Expanded(
              child: Text(
                'Role',
                style: TextStyle(fontStyle: FontStyle.italic),
              ),
            ),
          ),
        ],
        source: MyDataTableSource(),
      ),
    );
  }
}

class MyDataTableSource extends DataTableSource {
  @override
  DataRow getRow(int index) {
    return DataRow.byIndex(
      index: index,
      cells: <DataCell>[
        DataCell(GestureDetector(
          onTap: () => _MyHomePageState().update(),
          child: const Text('Sarah'),
        )),
        DataCell(GestureDetector(
          onTap: () => _MyHomePageState().update(),
          child: const Text('19'),
        )),
        DataCell(GestureDetector(
          onTap: () => _MyHomePageState().update(),
          child: const Text('Student'),
        )),
      ],
    );
  }

  @override
  bool get isRowCountApproximate => false;

  @override
  int get rowCount => 3;

  @override
  int get selectedRowCount => 0;
}
1

There are 1 answers

2
Abhi Tripathi On BEST ANSWER

You are triggering .update() on a new state reference. Use a proper state management or you're forced to use setState use "Key" like this

import 'package:flutter/material.dart';

GlobalKey<MyHomePageState> myHomePageKey = GlobalKey<MyHomePageState>();

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return  MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(key:myHomePageKey),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {

  @override
  setState(fn){
    if (mounted) {
      super.setState(fn);
    } else {
      debugPrint('Not mounted');
    }
  }

  update() => setState((){});

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Demo'),
      ),
      body: PaginatedDataTable(
        showCheckboxColumn: true,
        columns: const <DataColumn>[
          DataColumn(
            label: Expanded(
              child: Text(
                'Name',
                style: TextStyle(fontStyle: FontStyle.italic),
              ),
            ),
          ),
          DataColumn(
            label: Expanded(
              child: Text(
                'Age',
                style: TextStyle(fontStyle: FontStyle.italic),
              ),
            ),
          ),
          DataColumn(
            label: Expanded(
              child: Text(
                'Role',
                style: TextStyle(fontStyle: FontStyle.italic),
              ),
            ),
          ),
        ],
        source: MyDataTableSource(),
      ),
    );
  }
}

class MyDataTableSource extends DataTableSource {
    
  @override
  DataRow getRow(int index) {
    return DataRow.byIndex(
      index: index,
      cells: <DataCell>[
        DataCell(GestureDetector(
          onTap: () => myHomePageKey.currentState!.update(),
          child: const Text('Sarah'),
        )),
        DataCell(GestureDetector(
          onTap: () => myHomePageKey.currentState!.update(),
          child: const Text('19'),
        )),
        DataCell(GestureDetector(
          onTap: () => myHomePageKey.currentState!.update(),
          child: const Text('Student'),
        )),
      ],
    );
  }

  @override
  bool get isRowCountApproximate => false;

  @override
  int get rowCount => 3;

  @override
  int get selectedRowCount => 0;
}