我在 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),
),
);
}
}
我尝试实现 RawScrollbar,但它仍然给出相同的结果。滚动条不是从容器顶部开始。
我也尝试过与“Slivers”合作,但我没有一个很好的例子可以合作。如果有人可以为我提供一个简单的示例,其中包含带有文本的可滚动容器周围的滚动条,我也可以使用它。
对于将来访问此内容的任何人:我找到了解决方案。感谢这个线程: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 时这也是不可见的!