Skip to content

使用showAttach,绑定bindWidget后存在的异常情况 #288

@LiWenHui96

Description

@LiWenHui96

版本信息

  • Flutter版本:v3.38.6
  • flutter_smart_dialog版本:v4.9.8+9

描述bug/需求

A. 跳转新页面,布局为 ExtendedNestedScrollView + TabBarView,使用showAttach,绑定bindWidget报错。

Image

B. 不绑定时,切换Tab,提示

Image

且关闭页面时,提示

Image

问题demo

import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:sliver_tools/sliver_tools.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(colorScheme: .fromSeed(seedColor: Colors.deepPurple)),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
      builder: FlutterSmartDialog.init(
        builder: (BuildContext context, Widget? child) {
          return child ?? SizedBox.shrink();
        },
      ),
      navigatorObservers: <NavigatorObserver>[FlutterSmartDialog.observer],
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: Center(
        child: ElevatedButton(
          onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (context) => DemoPage(title: 'Demo'))),
          child: Text('跳转'),
        ),
      ),
    );
  }
}

class DemoPage extends StatefulWidget {
  const DemoPage({super.key, required this.title});

  final String title;

  @override
  State<DemoPage> createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  /// 用于 [ExtendedNestedScrollView] 的 key
  final GlobalKey<ExtendedNestedScrollViewState> key = GlobalKey<ExtendedNestedScrollViewState>();

  @override
  Widget build(BuildContext context) {
    final child = Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: ExtendedNestedScrollView(
        key: key,
        headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) => [
          SliverPinnedHeader(child: Container(height: 200, color: Colors.red)),
          SliverPinnedHeader(
            child: TabBar(tabs: List.generate(5, (index) => Tab(text: '${index + 1}'))),
          ),
        ],
        body: TabBarView(children: List.generate(5, (index) => TabBarIndexView(index: index + 1))),
        pinnedHeaderSliverHeightBuilder: () => 200,
        onlyOneScrollInBody: true,
      ),
    );

    return DefaultTabController(length: 5, child: child);
  }
}

class TabBarIndexView extends StatefulWidget {
  const TabBarIndexView({super.key, required this.index});

  final int index;

  @override
  State<StatefulWidget> createState() => _TabBarIndexViewState();
}

class _TabBarIndexViewState extends State<TabBarIndexView> with AutomaticKeepAliveClientMixin {
  @override
  Widget build(BuildContext context) {
    super.build(context);

    return ListView(children: [Dropdown(), Text('Tab ${widget.index}')]);
  }

  @override
  bool get wantKeepAlive => true;
}

class Dropdown extends StatefulWidget implements PreferredSizeWidget {
  const Dropdown({super.key});

  @override
  State<StatefulWidget> createState() => _DropdownState();

  @override
  Size get preferredSize => .fromHeight(34);
}

class _DropdownState extends State<Dropdown> {
  @override
  Widget build(BuildContext context) {
    return Container(
      height: 34,
      child: Row(
        spacing: 4,
        children: List<Widget>.generate(4, (index) {
          Widget child = Center(child: Text('Drop ${index + 1}'));

          child = ColoredBox(color: Colors.primaries[index % Colors.primaries.length], child: child);

          child = GestureDetector(onTap: () => openMenu(), child: child);

          return Expanded(child: child);
        }),
      ),
    );
  }

  /// 开启菜单
  Future<void> openMenu() async {
    SmartDialog.config.attach = SmartConfigAttach(maskTriggerType: .move);
    await SmartDialog.showAttach<void>(
      targetContext: context,
      builder: (BuildContext context) => Material(
        color: Colors.red,
        borderRadius: const .vertical(bottom: .circular(8)),
        child: Container(
          width: targetSize.width,
          constraints: BoxConstraints(
            maxHeight: (MediaQuery.heightOf(context) - targetOffset.dy - targetSize.height) / 3 * 2,
          ),
          child: Container(),
        ),
      ),
      maskIgnoreArea: Rect.fromLTRB(0, targetOffset.dy + targetSize.height, 0, 0),
      debounce: true,
      bindWidget: context,
    );
  }

  RenderBox get renderBox => context.findRenderObject()! as RenderBox;

  Offset get targetOffset => renderBox.localToGlobal(.zero);

  Size get targetSize => renderBox.size;
}

辛苦作者大大,帮忙看下。☺

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions