flutter 使小部件占用的空间比可用空间少一点(以考虑键盘上方的小部件)

1aaf6o9v  于 2023-05-30  发布在  Flutter
关注(0)|答案(1)|浏览(145)

有没有一种方法可以让小部件主体在键盘显示时占用的空间比可用空间少一点(考虑到键盘上方固定的BottomSheet)?
我有一个底部的工作表,我显示每当键盘可用,占用65 px的空间。
下面是一个TextFormField,下面有一个按钮。TextFormField会在我键入时添加额外的行,当我没有打开共享表时,最后一行在屏幕上保持可见。
我希望这个预期的空间是65 px短,所以TextField开始滚动65 px更快(占底部工作表附加在键盘上)。
有什么办法能让这件事成功吗?我当前的代码看起来应该可以工作,但是小部件仍然具有相同的滚动行为,就好像底部工作表不存在一样。
我尝试过的其他所有方法(为我的BoxConstraints小部件使用maxHeight,在列外添加Expanded小部件)都只会导致一堆错误,小部件无法正确加载。
当前小部件(Scaffold Stateful Widget是所有这些配置的地方):

class TweetDraftPage extends StatelessWidget {
  Thread? thread;
  TweetDraftPage({this.thread, super.key});

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider<ThreadWatcherBloc>(
          create: (context) => getIt<ThreadWatcherBloc>(),
        ),
        BlocProvider<ThreadEditorBloc>(
          create: (context) => ThreadEditorBloc(
            getIt<ThreadRepository>(),
            thread,
            getIt<ThreadWatcherBloc>(),
          ),
        ),
      ],
      child: TweetDraftPageScaffold(),
    );
  }
}

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

  @override
  State<TweetDraftPageScaffold> createState() => _TweetDraftPageScaffoldState();
}

class _TweetDraftPageScaffoldState extends State<TweetDraftPageScaffold>
    with WidgetsBindingObserver {
  bool isKeyboardVisible = false;
  double keyboardHeight = 0;
  final double bottomSheetHeight = 65;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance?.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance?.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeMetrics() {
    final mediaQueryData = MediaQuery.of(context);
    final keyboardVisibleHeight = mediaQueryData.viewInsets.bottom;
    setState(() {
      isKeyboardVisible = keyboardVisibleHeight > 0;
      keyboardHeight = keyboardVisibleHeight;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Theme.of(context).canvasColor,
      appBar: AppBar(
        title: const Text(
          'Edit Tweet',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: LayoutBuilder(builder: (context, constraints) {
          final double availableHeight = math.max(
            constraints.maxHeight -
                keyboardHeight -
                (isKeyboardVisible ? bottomSheetHeight : 0),
            0,
          );
          return SingleChildScrollView(
            child: ConstrainedBox(
              constraints:
                  BoxConstraints(minHeight: availableHeight),
              child: Column(
                children: [
                  // for each TweetEditorBloc in ThreadEditorBloc, create a BlocProvider
                  // with the TweetEditorBloc, and a child of TweetDraft
                  BlocConsumer<ThreadEditorBloc, ThreadEditorState>(
                    listenWhen: (p, c) =>
                        c.threadSaved != null && c.threadSaved != p.threadSaved,
                    listener: (context, state) {
                      Navigator.pop(context);
                    },
                    buildWhen: (p, c) =>
                        p.tweetEditorBlocs.length != c.tweetEditorBlocs.length,
                    builder: (context, state) {
                      return Form(
                        child: Column(
                          children: [
                            for (var i = 0;
                                i < state.tweetEditorBlocs.length;
                                i++)
                              BlocProvider<TweetEditorBloc>(
                                create: (context) => state.tweetEditorBlocs[i],
                                child: TweetDraft(),
                              ),
                          ],
                        ),
                      );
                    },
                  ),
                  SizedBox(height: 8),
                  ElevatedButton(
                      onPressed: () {
                        context
                            .read<ThreadEditorBloc>()
                            .add(const ThreadEditorEvent.saveThread());
                        context
                            .read<ThreadWatcherBloc>()
                            .add(const ThreadWatcherEvent.refresh());
                      },
                      child: Text('Save Draft')),
                ],
              ),
            ),
          );
        }),
      ),
      bottomSheet: isKeyboardVisible
          ? BottomSheet(
              constraints: BoxConstraints(maxHeight: bottomSheetHeight),
              shape: RoundedRectangleBorder(borderRadius: BorderRadius.zero),
              enableDrag: false,
              clipBehavior: Clip.none,
              onClosing: () => null,
              builder: (context) => Padding(
                  padding: EdgeInsets.fromLTRB(5, 0, 5, 0),
                  child: CharacterCounter()))
          : null,
    );
  }
}

图片

这是页面开始时的样子:

当我输入时,它会添加额外的行;如果它只是键盘,它会自动滚动,以便底部行(我正在键入的地方)总是可见的。在本例中,我输入的是第12行,它就在屏幕之外:

ttp71kqs

ttp71kqs1#

这里的主要问题是,我基本上是减去键盘的高度两次。
我所做的主要更改是(1)更新LayoutBuilder,使availableHeight只减去底部共享表的高度,而不是减去键盘高度,以及(2)将ConstrainedBox移动到SingleChildScrollView之外
我认为布局构建器中的约束已经考虑了keyboardHeight,所以我需要做的就是减去底部的共享表。

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

  @override
  State<TweetDraftPageScaffold> createState() => _TweetDraftPageScaffoldState();
}

class _TweetDraftPageScaffoldState extends State<TweetDraftPageScaffold>
    with WidgetsBindingObserver {
  bool isKeyboardVisible = false;
  double keyboardHeight = 0;
  final double bottomSheetHeight = 65;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance?.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance?.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeMetrics() {
    final mediaQueryData = MediaQuery.of(context);
    final keyboardVisibleHeight = mediaQueryData.viewInsets.bottom;

    setState(() {
      isKeyboardVisible = keyboardVisibleHeight > 0;
      keyboardHeight = keyboardVisibleHeight;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Theme.of(context).canvasColor,
      appBar: AppBar(
        title: const Text(
          'Edit Tweet',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        actions: [
          TextButton(
              onPressed: () {
                context
                    .read<ThreadEditorBloc>()
                    .add(const ThreadEditorEvent.saveThread());
                context
                    .read<ThreadWatcherBloc>()
                    .add(const ThreadWatcherEvent.refresh());
              },
              child: Text('Save'))
        ],
      ),
      body: LayoutBuilder(builder: (context, constraints) {
        final double availableHeight = math.max(
          constraints.maxHeight - (isKeyboardVisible ? bottomSheetHeight : 0),
          0,
        );
        print('availableHeight: $availableHeight');
        return ConstrainedBox(
          constraints: BoxConstraints(maxHeight: availableHeight),
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: SingleChildScrollView(
              child: // Contents of the widget
            ),
          ),
        );
      }),
      bottomSheet: isKeyboardVisible
          ? BottomSheet(
              constraints: BoxConstraints(maxHeight: bottomSheetHeight),
              shape: RoundedRectangleBorder(borderRadius: BorderRadius.zero),
              enableDrag: false,
              clipBehavior: Clip.none,
              onClosing: () => null,
              builder: (context) => Padding(
                  padding: EdgeInsets.fromLTRB(5, 0, 5, 0),
                  child: CharacterCounter()))
          : null,
    );
  }
}

相关问题