material_table_view icon indicating copy to clipboard operation
material_table_view copied to clipboard

SliverMainAxisGroup and SliveTableView weird behavior with keyboard and scroll

Open WisyNim opened this issue 3 months ago • 1 comments

I'm seeing some odd behavior with the keyboard and scroll position when using a SliverTableView inside a SliverMainAxisGroup that has more than one SliverToBoxAdapter.

As you can see in the attached video, when a TextField is tapped, the keyboard appears and then immediately hides, causing the list to scroll to the end.

https://github.com/user-attachments/assets/58203265-528c-4bcb-80a5-d406338e8776

Code to reproduce

import 'package:flutter/material.dart';
import 'package:material_table_view/material_table_view.dart';
import 'package:material_table_view/sliver_table_view.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sliver Table Bug Demo',
      theme: ThemeData(primarySwatch: Colors.blue, useMaterial3: true),
      // --- CHOOSE WHICH PAGE TO DISPLAY ---
      // home: BuggySliverTablePage(),
      home: BuggySliverTablePage(),
    );
  }
}

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

  @override
  State<BuggySliverTablePage> createState() => _BuggySliverTablePageState();
}

class _BuggySliverTablePageState extends State<BuggySliverTablePage> {
  // Simple data for the table
  final List<String> _items = List.generate(
    50,
    (index) => 'Item Row ${index + 1}',
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Buggy Example'),
        backgroundColor: Colors.red[400],
      ),
      body: SafeArea(
        child: Column(
          children: [
            Expanded(
              child: CustomScrollView(
                slivers: [
                  // This group introduces an extra layer that, when rebuilt,
                  // can cause the state of its children (the table) to be lost.
                  SliverMainAxisGroup(
                    slivers: [
                      SliverToBoxAdapter(
                        child: Container(
                          padding: const EdgeInsets.all(16),
                          color: Colors.red[50],
                          child: const Text(
                            'This is a header widget. When the keyboard appears, this section rebuilds, causing the SliverMainAxisGroup to be recreated, which in turn destroys and recreates the table below.',
                            textAlign: TextAlign.center,
                          ),
                        ),
                      ),
                      SliverToBoxAdapter(
                        child: Container(
                          padding: const EdgeInsets.all(16),
                          color: Colors.red[50],
                          child: const Text(
                            'This is a header widget. When the keyboard appears, this section rebuilds, causing the SliverMainAxisGroup to be recreated, which in turn destroys and recreates the table below.',
                            textAlign: TextAlign.center,
                          ),
                        ),
                      ),
                      SliverTableView.builder(
                        // ANTI-PATTERN: The 'columns' list is recreated on every build.
                        // When the parent rebuilds, Flutter sees a new list and
                        // destroys the old table state to build a new one.
                        columns: [
                          const TableColumn(width: 200),
                          const TableColumn(width: 200),
                        ],
                        rowCount: _items.length,
                        rowHeight: 64,
                        rowBuilder: (context, row, contentBuilder) {
                          return contentBuilder(context, (context, column) {
                            if (column == 0) {
                              return Padding(
                                padding: const EdgeInsets.all(8.0),
                                child: Text(_items[row]),
                              );
                            } else {
                              return const Padding(
                                padding: EdgeInsets.all(8.0),
                                child: TextField(
                                  decoration: InputDecoration(
                                    border: OutlineInputBorder(),
                                    labelText: 'Enter value',
                                  ),
                                ),
                              );
                            }
                          });
                        },
                      ),
                    ],
                  ),
                ],
              ),
            ),
            Container(
              decoration: const BoxDecoration(
                color: Colors.white,
                border: Border(top: BorderSide(color: Colors.grey)),
              ),
              padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
              child: const Text(
                'Tap a text field above to show the keyboard. Notice how the table loses its state and scroll position when the keyboard appears.',
                textAlign: TextAlign.center,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

WisyNim avatar Aug 26 '25 23:08 WisyNim

Good catch! It looks like the framework is trying to scroll to make a SliverTableView fit onto the screen resulting in scrolling to the very end and the originally clicked TextField closer to the top getting destroyed as it gets scrolled way off the screen.

I'll see if I can spare some time on investigating solutions to this problem. Let me know if I've missed anything.

NikolayNIK avatar Sep 15 '25 09:09 NikolayNIK