#flutter #flutter-layout #nestedscrollview #sliverappbar
#flutter #flutter-layout #вложенный scrollview #панель слайверов
Вопрос:
Я пытаюсь добиться поведения прокрутки, как в gif по этой ссылке. При прокрутке скрывается слайдер изображения, а заголовок продукта переходит в заголовок панели приложений. Также есть фиксированная кнопка Добавить в пакет, которая фиксирована, но прокручивается вместе с макетом в определенной позиции экрана.
Я мог бы показывать и скрывать кнопку добавления в пакет, используя vising visibility_detector. При медленной прокрутке он работает, но при быстрой прокрутке кнопка не видна.
Я мог бы добиться только этого
Я пробовал, как показано ниже:
Scaffold(
body: SafeArea(
child: CustomAppBar(
centerTitle: false,
expandedHeight: 355,
searchIconShow: true,
showBackButton: true,
leadingWidget: const Icon(Icons.arrow_back),
titleWidget: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
top = constraints.biggest.height;
return top < 280
? FlexibleSpaceBar(
centerTitle: false,
titlePadding: const EdgeInsets.all(15),
title: Container(
width: MediaQuery.of(context).size.width * 0.57,
height: 60,
padding: const EdgeInsets.fromLTRB(35, 0, 0, 0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text("M.A.C Prep Prep Prime Fix -Original",
style: TextStyle(
overflow: TextOverflow.ellipsis,
color: Colors.black,
fontWeight: FontWeight.normal,
fontSize: 14)),
Expanded(
child: Row(
children: const [
Icon(Icons.star, size: 8, color: Colors.grey),
SizedBox(width: 2),
Text(
"4.1",
style: TextStyle(
color: Colors.grey, fontSize: 10),
),
SizedBox(width: 5),
Icon(Icons.circle, size: 5, color: Colors.grey),
SizedBox(width: 5),
Text("Rs 1200",
style: TextStyle(
color: Colors.grey, fontSize: 10))
],
),
)
],
),
),
background: Container())
: FlexibleSpaceBar(
centerTitle: false,
titlePadding: const EdgeInsets.all(15),
title: SingleChildScrollView(
physics:const NeverScrollableScrollPhysics(),
child: Container(
height:342,
padding: const EdgeInsets.fromLTRB(0, 135, 0, 0),
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
_buildSlider(),
Row(
children: const [
Expanded(
child: Text(
"M.A.C Prep Prep Prime Fix -Original",
style: TextStyle(
overflow: TextOverflow.ellipsis,
color: Colors.black,
fontWeight: FontWeight.normal,
fontSize: 12)),
),
Icon(
Icons.share,
color: Colors.black,
size: 20,
)
],
),
Row(
children: const [
Icon(Icons.star, size: 10, color: Colors.grey),
SizedBox(width: 2),
Text(
"4.1",
style:
TextStyle(color: Colors.grey, fontSize: 10),
),
SizedBox(width: 5),
Icon(Icons.circle, size: 5, color: Colors.grey),
SizedBox(width: 5),
Text("Rs 1200",
style: TextStyle(
color: Colors.grey, fontSize: 10))
],
)
],
),
),
),
);
}),
myWidget: Stack(children: [
SingleChildScrollView(
child: Column(
children: [
Container(height: 200, color: Colors.green),
Container(height: 200, color: Colors.yellow),
Container(height: 200, color: Colors.pink),
Container(height: 200, color: Colors.grey),
Container(height: 200, color: Colors.blueGrey),
Container(height: 200, color: Colors.indigo),
Container(height: 200, color: Colors.purple),
Container(height: 200, color: Colors.green),
Container(height: 200, color: Colors.yellow),
Container(height: 200, color: Colors.pink),
Container(height: 200, color: Colors.grey),
VisibilityDetector(
key: Key('my-widget-key'),
onVisibilityChanged: (visibility) {
var visiblePercentage =
visibility.visibleFraction * 100;
if (visiblePercentage < 0) {
setState(() {
showBottomButton = false;
});
}
else{
setState(() {
showBottomButton = true;
});
}
},
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Container(
padding: const EdgeInsets.all(20),
width: MediaQuery.of(context).size.width,
color: Colors.red,
child: const Text("Add to Bag",
style: TextStyle(color: Colors.white))),
),
),
VisibilityDetector(
key: Key('my-widget-key2'),
onVisibilityChanged: (visibility) {
var visiblePercentage =
visibility.visibleFraction * 100;
if (visiblePercentage < 0) {
setState(() {
showBottomButton = true;
});
} else {
setState(() {
showBottomButton = false;
});
}
},
child: Column(
children: [
Container(height: 200, color: Colors.black),
Container(height: 200, color: Colors.indigo),
Container(height: 200, color: Colors.purple),
Container(height: 200, color: Colors.indigo),
Container(height: 200, color: Colors.purple),
Container(height: 200, color: Colors.indigo),
Container(height: 200, color: Colors.black),
Container(height: 200, color: Colors.purple),
Container(height: 200, color: Colors.black),
Container(height: 200, color: Colors.purple),
Container(height: 200, color: Colors.black),
Container(height: 200, color: Colors.purple),
Container(height: 200, color: Colors.black),
Container(height: 200, color: Colors.purple),
Container(height: 200, color: Colors.black),
Container(height: 200, color: Colors.purple),
],
)),
],
),
),
Visibility(
visible: showBottomButton,
child: Align(
alignment: Alignment.bottomCenter,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
padding: const EdgeInsets.all(20),
width: MediaQuery.of(context).size.width,
color: Colors.red,
child: const Text("Add to Bag",
style: TextStyle(color: Colors.white))),
),
),
),
]),
),
),
);
И я смог добиться только этого
Ответ №1:
Вы можете сделать это с помощью CustomScrollView и Slivers простым способом:
Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
backgroundColor: Colors.red,
expandedHeight: 200.0, // This hand the expanded height of the header
flexibleSpace: FlexibleSpaceBar(
background: Stack(
alignment: AlignmentDirectional.center,
children: [
getMyContent(), // your content to show on header here
],
)),
),
// The items that you show down
SliverFixedExtentList(
itemExtent: 150.0,
delegate: SliverChildBuilderDelegate(
(context, index) => getMyList(item: list[index]),
childCount: list.length),
),
],
));
Комментарии:
1. как исправить и прокрутить контейнер, когда он достигнет некоторой позиции?
Ответ №2:
Вы можете достичь этого результата с помощью Listview и SliverAppBar. Я делюсь demo
тем же, что и вы customize
, в соответствии с вашим выбором.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ScrollController _controller = ScrollController();
ScrollController _controller1 = ScrollController();
@override
void initState() {
super.initState();
_controller.addListener(listenChanges);
_controller1.addListener(listListenChanges);
}
bool showTitle = false;
bool isPrimary = false;
void listenChanges() {
if (_controller.offset >= 170) {
showTitle = true;
} else {
showTitle = false;
}
if(_controller.offset == 0.0){
isPrimary = false;
}
setState(() {});
}
void listListenChanges() {
print(_controller1.offset == _controller1.position.maxScrollExtent);
print(_controller1.offset == 0.0);
print(_controller1.offset);
print(_controller1.position.maxScrollExtent);
if (_controller1.offset == _controller1.position.maxScrollExtent) {
isPrimary = true;
} else {
isPrimary = false;
}
setState(() {});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Builder(builder: (context) => Scaffold(
body: NestedScrollView(
headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) => [SliverAppBar(
// collapsedHeight: 70,
pinned: true,
title: Visibility(
visible: showTitle,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"This is your fixed header ",
style:
TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
),
Text(
"This is your fixed header ",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Colors.grey),
),
],
),
),
backgroundColor: Colors.red,
expandedHeight:
170.0, // This hand the expanded height of the header
flexibleSpace: FlexibleSpaceBar(
background: Stack(
alignment: AlignmentDirectional.center,
children: [
Card(
child: Text(
"This is your fixed header ",
style:
TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
), // your content to show on header here
],
)),
)],
controller: _controller,
body: ListView(
physics: NeverScrollableScrollPhysics(),
primary: false,
shrinkWrap: true,
children: [
Container(
color: Colors.yellow,
height: MediaQuery.of(context).size.height - 270,
child: Stack(
children: [
Positioned(
top: 0,
left: 0,
bottom: 40,
width: MediaQuery.of(context).size.width,
child: ListView.builder(
physics: isPrimary ? NeverScrollableScrollPhysics() : null,
controller: _controller1,
itemBuilder:
(context, index) => Container(height: 100,child: Text("sssssss")),
itemCount: 15)),
Positioned(
bottom: 10,
left: 20,
height: 35,
right: 20,
child: ElevatedButton(onPressed: (){},child: Text("button"),)),
],
),
),
Padding(
padding: const EdgeInsets.all(15.0),
child: Text("Text 1"),
),
Padding(
padding: const EdgeInsets.all(15.0),
child: Text("Text 1"),
),
Padding(
padding: const EdgeInsets.all(15.0),
child: Text("Text 1"),
)
],
),
))));
}
}
Комментарии:
1. Я уже добился этого, моя настоящая проблема ниже, есть кнопка добавления в сумку, которую необходимо прокручивать при достижении некоторого положения, пока ее не нужно исправить. Пожалуйста, посмотрите на мой ожидаемый gif красиво
2. Пожалуйста, проверьте обновленный ответ, просто скопируйте и вставьте весь код и запустите его.
3. Вы проверили?
4. да, но это не все, что мне нужно
5. в чем проблема сейчас?
Ответ №3:
Решаемая проблема была в логике. Просто изменен if (visiblePercentage < 0)
на
if (visiblePercentage <= 0)