Flutter ScrollController не прокручивает до конца экранаAndroid

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Flutter ScrollController не прокручивает до конца экрана

Сообщение Anonymous »


I've encountered a weird issue in my Flutter code that I will try to describe - maybe somebody will have an answer for that.

I have a ListView (called _MessageList) with chat bubbles that I want to scroll to the bottom of. This ListView is nested inside my custom Scaffold with AppBar widget called ScreenWithAppBar. I have added a ScrollController to the ScreenWithAppBar and added the method below to the initState method of the chat screen.

SchedulerBinding.instance.addPostFrameCallback( (_) { scrollController.animateTo( scrollController.position.maxScrollExtent, duration: const Duration(milliseconds: 2000), curve: Curves.easeOut, ); }, ); After entering the screen however, it only scrolls a little tiny bit until the app bar disappears and then stops. I have tried adding the scroll controller to the ListView alone itself, but it does NOT work.

Here is a gif of my issue:
Изображение


What can I do to scroll to the bottom of the screen? I am attaching the whole code for the chat widgets and also for my ScreenWithAppBar widget.

ChatScreen widget - this is a screen that contains the list with chat bubbles:

class ChatScreen extends StatefulWidget { const ChatScreen({ super.key, required this.room, }); final RoomModel room; @override State createState() => _ChatScreenState(); } class _ChatScreenState extends State { late TextEditingController messageController; late ScrollController scrollController; late String userId; @override void initState() { super.initState(); messageController = TextEditingController(); scrollController = ScrollController(); userId = context.read().getCurrentUserId(); SchedulerBinding.instance.addPostFrameCallback( (_) { scrollController.animateTo( scrollController.position.maxScrollExtent, duration: const Duration(milliseconds: 300), curve: Curves.easeOut, ); }, ); } @override Widget build(BuildContext context) { return ScreenWithAppBar( scrollController: scrollController, title: widget.room.users.firstWhere( (String element) => element != userId, ), bottomNavigationBar: _MessageInputBar( messageController: messageController, scrollController: scrollController, room: widget.room, userId: userId, ), body: StreamBuilder( initialData: widget.room, stream: FirebaseUtils.firestore.getSnapshotStream( path: '/chatRooms', uid: widget.room.uid, ), builder: (BuildContext context, AsyncSnapshot snapshot) { return StreamBuilder( stream: context.read().getMessageStream( room: widget.room, ), builder: ( BuildContext context, AsyncSnapshot snapshot, ) { if (snapshot.hasData) { return _MessageList( messageList: snapshot.data!, ); } else { return const Center( child: CircularProgressIndicator(), ); } }, ); }, ), ); } @override void dispose() { messageController.dispose(); scrollController.dispose(); super.dispose(); } } _MessageList - widget that contains a ListView that generates the chat bubbles:

class _MessageList extends StatelessWidget { const _MessageList({ required this.messageList, }); final List messageList; @override Widget build(BuildContext context) { return ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: messageList.length, itemBuilder: (BuildContext context, int index) { final MessageModel message = messageList[index]; return Padding( padding: const EdgeInsets.all(8.0), child: _MessageBubble( message: message, ), ); }, ); } } ScreenWithAppBar - a custom built generic entry screen that I use everywhere:

class ScreenWithAppBar extends StatelessWidget { const ScreenWithAppBar({ this.showAppBar = true, this.title, this.centerTitle = false, this.appBarTransparent = true, this.appBarShowBackButton = true, this.actions, this.bottom, this.backButtonAlwaysEnabled = false, this.body, this.customPadding = 0.0, this.physics = const BouncingScrollPhysics(), this.isBodyScrollable = true, this.extendBody = false, this.extendBodyBehindAppBar = false, this.scrollController, this.fab, this.fabLocation = FloatingActionButtonLocation.endFloat, this.bottomNavigationBar, this.onWillPop, this.onRefresh, this.notificationPredicate, this.isHome = false, this.isPrimaryColor = false, this.isNestedScrollEnabled = true, this.resizeToAvoidBottomInset = true, this.usePageView = false, this.pageViewController, this.pageViewChildren, super.key, }) : assert(!backButtonAlwaysEnabled || onWillPop != null); /// App Bar final bool showAppBar; final String? title; final bool centerTitle; final bool appBarTransparent; final bool appBarShowBackButton; final List? actions; final PreferredSizeWidget? bottom; final bool backButtonAlwaysEnabled; /// Body final Widget? body; final double customPadding; final ScrollPhysics physics; final bool isBodyScrollable; final bool extendBody; final bool extendBodyBehindAppBar; final ScrollController? scrollController; /// Fab final Widget? fab; final FloatingActionButtonLocation fabLocation; /// BottomNavBar final Widget? bottomNavigationBar; /// Callbacks final Future Function()? onWillPop; final Future Function()? onRefresh; final bool Function(ScrollNotification)? notificationPredicate; /// Flags final bool isHome; final bool isPrimaryColor; final bool isNestedScrollEnabled; final bool resizeToAvoidBottomInset; /// PageView final bool usePageView; final PageController? pageViewController; final List? pageViewChildren; @override Widget build(BuildContext context) { return WillPopScope( onWillPop: onWillPop, child: Scaffold( extendBody: extendBody, extendBodyBehindAppBar: extendBodyBehindAppBar, resizeToAvoidBottomInset: resizeToAvoidBottomInset, appBar: isNestedScrollEnabled ? null : showAppBar ? PreferredSize( preferredSize: Size.fromHeight( AppThemes.dimens.appBarHeight, ), child: ChatifyAppBar( isSliver: false, title: title, centerTitle: centerTitle, transparent: appBarTransparent, showBackButton: appBarShowBackButton, actions: actions, isPrimaryColor: isPrimaryColor, bottom: bottom, onPop: onWillPop, backButtonAlwaysEnabled: backButtonAlwaysEnabled, ), ) : null, bottomNavigationBar: bottomNavigationBar, body: SafeArea( child: _ConditionalRefreshIndicator( notificationPredicate: notificationPredicate, onRefresh: onRefresh, isNestedScrollEnabled: isNestedScrollEnabled, usePageView: usePageView, child: _ScreenWithAppBarBody( title: title, centerTitle: centerTitle, body: body, customPadding: customPadding, appBarShowBackButton: appBarShowBackButton, isHome: isHome, showAppBar: showAppBar, appBarTransparent: appBarTransparent, actions: actions, isPrimaryColor: isPrimaryColor, isNestedScrollEnabled: isNestedScrollEnabled, isBodyScrollable: isBodyScrollable, physics: physics, usePageView: usePageView, pageViewController: pageViewController, pageViewChildren: pageViewChildren, scrollController: scrollController, bottom: bottom, backButtonAlwaysEnabled: backButtonAlwaysEnabled, onPop: onWillPop, ), ), ), ), ); } } The body of the widget above:

class _ScreenWithAppBarBody extends StatelessWidget { const _ScreenWithAppBarBody({ required this.body, required this.appBarTransparent, required this.appBarShowBackButton, required this.isHome, required this.showAppBar, required this.physics, required this.customPadding, required this.centerTitle, required this.isPrimaryColor, required this.isNestedScrollEnabled, required this.isBodyScrollable, required this.usePageView, required this.title, required this.actions, required this.pageViewController, required this.pageViewChildren, required this.scrollController, required this.bottom, required this.backButtonAlwaysEnabled, required this.onPop, }); final Widget? body; final bool appBarTransparent; final bool appBarShowBackButton; final bool isHome; final bool showAppBar; final ScrollPhysics physics; final double customPadding; final bool centerTitle; final bool isPrimaryColor; final bool isNestedScrollEnabled; final bool isBodyScrollable; final bool usePageView; final bool backButtonAlwaysEnabled; final String? title; final List? actions; final PageController? pageViewController; final List? pageViewChildren; final ScrollController? scrollController; final PreferredSizeWidget? bottom; final Future Function()? onPop; @override Widget build(BuildContext context) { return _ConditionalNestedScrollView( isNestedScrollEnabled: isNestedScrollEnabled && isBodyScrollable, isHome: isHome, showAppBar: showAppBar, title: title, centerTitle: centerTitle, appBarTransparent: appBarTransparent, appBarShowBackButton: appBarShowBackButton, actions: actions, isPrimaryColor: isPrimaryColor, scrollController: scrollController, bottom: bottom, backButtonAlwaysEnabled: backButtonAlwaysEnabled, onPop: onPop, body: _ConditionalPageView( isNestedScrollEnabled: isNestedScrollEnabled, isBodyScrollable: isBodyScrollable, physics: physics, customPadding: customPadding, body: body, usePageView: usePageView, pageViewController: pageViewController, pageViewChildren: pageViewChildren, scrollController: scrollController, ), ); } } NestedScrollView inside of the body above:

class _ConditionalNestedScrollView extends StatelessWidget { const _ConditionalNestedScrollView({ required this.body, required this.appBarTransparent, required this.appBarShowBackButton, required this.isHome, required this.showAppBar, required this.centerTitle, required this.isPrimaryColor, required this.isNestedScrollEnabled, required this.title, required this.actions, required this.scrollController, required this.bottom, required this.backButtonAlwaysEnabled, required this.onPop, }); final Widget body; final bool appBarTransparent; final bool appBarShowBackButton; final bool isHome; final bool showAppBar; final bool centerTitle; final bool isPrimaryColor; final bool isNestedScrollEnabled; final bool backButtonAlwaysEnabled; final String? title; final List? actions; final ScrollController? scrollController; final PreferredSizeWidget? bottom; final Future Function()? onPop; @override Widget build(BuildContext context) { if (isNestedScrollEnabled) { return NestedScrollView( floatHeaderSlivers: true, controller: scrollController, headerSliverBuilder: (_, __) => [ if (isHome && showAppBar) ChatifyAppBar( title: title ?? context.tr.app_name, centerTitle: centerTitle, transparent: appBarTransparent, showBackButton: false, actions: actions, isPrimaryColor: isPrimaryColor, bottom: bottom, ) else if (showAppBar) ChatifyAppBar( title: title, centerTitle: centerTitle, transparent: appBarTransparent, showBackButton: appBarShowBackButton, actions: actions, isPrimaryColor: isPrimaryColor, bottom: bottom, backButtonAlwaysEnabled: backButtonAlwaysEnabled, onPop: onPop, ), ], body: body, ); } else { return body; } } } PageView inside of the body above:

class _ConditionalPageView extends StatelessWidget { const _ConditionalPageView({ required this.body, required this.customPadding, required this.physics, required this.isNestedScrollEnabled, required this.isBodyScrollable, required this.usePageView, required this.pageViewController, required this.pageViewChildren, required this.scrollController, }); final Widget? body; final double customPadding; final ScrollPhysics physics; final bool isNestedScrollEnabled; final bool isBodyScrollable; final bool usePageView; final PageController? pageViewController; final List? pageViewChildren; final ScrollController? scrollController; @override Widget build(BuildContext context) { if (usePageView) { return PageView.builder( controller: pageViewController, physics: const NeverScrollableScrollPhysics(), itemBuilder: (BuildContext context, int index) { return _SingleChildScrollView( scrollController: scrollController, physics: physics, isNestedScrollEnabled: isNestedScrollEnabled, isBodyScrollable: isBodyScrollable, customPadding: customPadding, body: pageViewChildren![index], ); }, ); } else { return _SingleChildScrollView( scrollController: scrollController, physics: physics, isNestedScrollEnabled: isNestedScrollEnabled, isBodyScrollable: isBodyScrollable, customPadding: customPadding, body: body!, ); } } } Inside of the widget above - used to display child of the ScreenWithAppBar:

class _SingleChildScrollView extends StatelessWidget { const _SingleChildScrollView({ required this.body, required this.customPadding, required this.physics, required this.isNestedScrollEnabled, required this.isBodyScrollable, required this.scrollController, }); final Widget body; final double customPadding; final ScrollPhysics physics; final bool isNestedScrollEnabled; final bool isBodyScrollable; final ScrollController? scrollController; @override Widget build(BuildContext context) { if (isBodyScrollable) { return SingleChildScrollView( controller: isNestedScrollEnabled ? null : scrollController, physics: physics, child: _Body( customPadding: customPadding, body: body, ), ); } else { return _Body( customPadding: customPadding, body: body, ); } } } Custom body with shimmer of the widget above:

class _Body extends StatelessWidget { const _Body({ required this.body, required this.customPadding, }); final Widget body; final double customPadding; @override Widget build(BuildContext context) { return ScreenPadding( customPadding: customPadding, child: Shimmer( child: body, ), ); } } Sorry for such a lengthy message and code, I am in a pickle and don't really know how to go from here. Any help would be much appreciated!


Источник: https://stackoverflow.com/questions/772 ... the-screen
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»