带有 SingleChildScrollView 的滚动条未从滚动容器的顶部开始

问题描述 投票:0回答:1

我在 Flutter 中创建了一个测验应用程序。在测验开始之前,玩家将进入特定测验的开始页面。该页面包含带有标题和背景图像的标题,以及带有测验描述和“开始测验”按钮的正文。有时描述很长,或者玩家的字体大小比平常大,所以我使用 SingleChildScrollView 将描述放入可滚动容器中。

一切都很好,我已经和一大群人一起测试过。但有一些评论称,有些人没有阅读整个描述,因为他们不知道可以滚动。所以我的解决方案当然是一个可见的滚动条,这样用户直观地感觉到他们可以在描述中滚动。 (顺便说一句,这也是问题有点太长的情况,或者在字体较大的情况下溢出的情况)。

我尝试在 SingleChildScrollView 周围实现滚动条小部件,以使其明显表明它是一个可滚动容器,并且有更多可用信息,但轨道不是从可滚动容器的顶部开始,而是从这中间。 (问题和答案也是如此,我在可滚动的列中有一个答案列表)。

这是我的简化代码(滚动条用箭头“--->”指示):

import 'package:Client/hive/quiz.dart' as quizmodel;
import 'package:Client/screens/play/quiz/quiz_question_page.dart';
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:iconsax/iconsax.dart';
import 'package:Client/constants.dart';

import 'package:shared_preferences/shared_preferences.dart';

class QuizStartPage2 extends StatefulWidget {
  const QuizStartPage2({super.key, required this.quiz});
  final quizmodel.Quiz quiz;
  @override
  State<QuizStartPage2> createState() => _QuizStartPage2State();
}

class _QuizStartPage2State extends State<QuizStartPage2> {
  final ScrollController scrollController = ScrollController();

  @override
  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;

    return Scaffold(
      backgroundColor: const Color.fromARGB(255, 245, 245, 245),
      appBar: AppBar(
        toolbarHeight: 80,
        flexibleSpace: Container(
          decoration: const BoxDecoration(
            gradient: LinearGradient(
                begin: Alignment.center,
                end: Alignment.bottomCenter,
                colors: <Color>[Colors.black, Colors.transparent]),
          ),
        ),
        backgroundColor: Colors.transparent,
        elevation: 0,
        leadingWidth: 56 + defaultPadding,
        centerTitle: true,
        title: AutoSizeText(
          "QuizTitle",
          maxLines: 2,
          minFontSize: 16,
          style: GoogleFonts.dmSans(
            fontSize: 30,
            height: 1,
            fontWeight: FontWeight.bold,
            color: Colors.white,
            shadows: <Shadow>[
              Shadow(
                offset: Offset(2.0, 4.0),
                blurRadius: 10.0,
                color: Color.fromARGB(30, 0, 0, 0),
              ),
            ],
          ),
        ),
        leading: Padding(
          padding: const EdgeInsets.only(
              left: defaultPadding,
              top: defaultPadding * 0.5,
              bottom: defaultPadding * 0.5),
          child: Container(
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.circular(20),
            ),
            child: IconButton(
              onPressed: () {
                Navigator.pop(context);
              },
              icon: const Icon(
                Iconsax.arrow_left_2,
                color: Colors.black,
              ),
            ),
          ),
        ),
      ),
      extendBodyBehindAppBar: true,
      body: Stack(
        children: [
          Container(
              height: size.height * 0.5,
              width: size.width,
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  begin: Alignment.topCenter,
                  end: Alignment.bottomCenter,
                  colors: [Colors.black, Colors.black.withOpacity(0.65)],
                ),
              ),
              child: Image(
                image: NetworkImage(widget.quiz.image),
                fit: BoxFit.cover,
                alignment: Alignment.center,
                errorBuilder: (BuildContext context, Object exception,
                    StackTrace? stackTrace) {
                  // Return an empty container when an error occurs (e.g., invalid image)
                  return Container(
                    color: Colors.blueGrey,
                  );
                },
              )),
          Container(
            height: size.height * 0.5,
            width: size.width,
            decoration: BoxDecoration(
              gradient: LinearGradient(
                begin: Alignment.topCenter,
                end: Alignment.bottomCenter,
                colors: [Colors.black, Colors.black.withOpacity(0.65)],
              ),
            ),
          ),
          Align(
            alignment: Alignment.bottomCenter,
            child: Container(
                height: size.height * 0.6,
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.vertical(
                      top: Radius.circular(defaultBorderRadius * 3)),
                  color: Colors.white,
                ),
                child: Padding(
                  padding: const EdgeInsets.all(defaultPadding),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    mainAxisAlignment: MainAxisAlignment.start,
                    children: [
                      Expanded(
---->                   child: Scrollbar(
                          thumbVisibility: true,
                          trackVisibility: true,
                          child: SingleChildScrollView(
                            controller: scrollController,
                            physics: ScrollPhysics(),
                            padding: EdgeInsets.all(0),
                            child: AutoSizeText(
                              "This is the description of the quiz, I made it extra long to see the effect of the scrollbar it is currently having. This is not the behaviour I wanted to have and instead I want the scrollbar to start at the top of the scrollable-container. This is the description of the quiz, I made it extra long to see the effect of the scrollbar it is currently having. This is not the behaviour I wanted to have and instead I want the scrollbar to start at the top of the scrollable-container. Lorem Ipsum solor set amet Lorem Ipsum solor set amet Lorem Ipsum solor set amet .",
                              minFontSize: 12,
                              style: GoogleFonts.dmSans(
                                fontSize: 18.0,
                                fontWeight: FontWeight.bold,
                              ),
                              textAlign: TextAlign.start,
                            ),
                          ),
                        ),
                      ),
                      SafeArea(
                        top: false,
                        child: GestureDetector(
                          onTap: startQuiz,
                          child: Container(
                              decoration: BoxDecoration(
                                borderRadius: BorderRadius.all(
                                    Radius.circular(defaultBorderRadius * 3)),
                                color: Colors.black,
                              ),
                              child: Padding(
                                padding: const EdgeInsets.symmetric(
                                    horizontal: defaultPadding * 3,
                                    vertical: defaultPadding * 1.5),
                                child: Text("Start Quiz",
                                    textAlign: TextAlign.center,
                                    style: GoogleFonts.dmSans(
                                        fontSize: 18.0,
                                        fontWeight: FontWeight.bold,
                                        color: Colors.white)),
                              )),
                        ),
                      ),
                    ],
                  ),
                )),
          ),
        ],
      ),
    );
  }

  _setBeginTime() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString('started_at', DateTime.now().toString());
    /* print("Quiz started at: ${prefs.getString('started_at')}"); */
  }

  Future<void> startQuiz() async {
    _setBeginTime();

    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => QuizQuestionPage(quiz: widget.quiz),
      ),
    );
  }
}

Screenshot: notice scrollbar not beginning at the top

我尝试实现 RawScrollbar,但它仍然给出相同的结果。滚动条不是从容器顶部开始。

我也尝试过与“Slivers”合作,但我没有一个很好的例子可以合作。如果有人可以为我提供一个简单的示例,其中包含带有文本的可滚动容器周围的滚动条,我也可以使用它。

flutter scrollbar scrollable singlechildscrollview
1个回答
0
投票

对于将来访问此内容的任何人:我找到了解决方案。感谢这个线程:https://stackoverflow.com/a/64405574/17211590

使用 MediaQuery.removePadding 小部件和 removeTop: true

与滚动条一起使用

Expanded(
 child: MediaQuery.removePadding( // Added this widget around the Scrollbar
 context: context,
 removeTop: true, // Set this to "true"
 child: Scrollbar(
   controller: _scrollController,
   child: SingeChildScrollView(
    controller: _scrollController,
    ...

也适用于 RawScrollbar

Expanded(
 child: MediaQuery.removePadding(
 context: context,
 removeTop: true,
 child: RawScrollbar(
    ...

由于某种原因,flutter 添加了顶部填充,在我的例子中,它相对于可滚动文本来说是巨大的。使用 DevTools 时这也是不可见的!

© www.soinside.com 2019 - 2024. All rights reserved.