💄 Better scrolling
This commit is contained in:
		@@ -75,94 +75,76 @@ class _ContactScreenState extends State<ContactScreen> {
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          return SafeArea(
 | 
			
		||||
            child: NestedScrollView(
 | 
			
		||||
              headerSliverBuilder: (context, innerBoxIsScrolled) {
 | 
			
		||||
                return [
 | 
			
		||||
                  SliverOverlapAbsorber(
 | 
			
		||||
                    handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
 | 
			
		||||
                        context),
 | 
			
		||||
                    sliver: SliverAppBar(
 | 
			
		||||
                      title: Text('contact'.tr),
 | 
			
		||||
                      centerTitle: false,
 | 
			
		||||
                      titleSpacing:
 | 
			
		||||
                          SolianTheme.isLargeScreen(context) ? null : 24,
 | 
			
		||||
                      forceElevated: innerBoxIsScrolled,
 | 
			
		||||
                      actions: [
 | 
			
		||||
                        const NotificationButton(),
 | 
			
		||||
                        PopupMenuButton(
 | 
			
		||||
                          icon: const Icon(Icons.add_circle),
 | 
			
		||||
                          itemBuilder: (BuildContext context) => [
 | 
			
		||||
                            PopupMenuItem(
 | 
			
		||||
                              child: ListTile(
 | 
			
		||||
                                title: Text('channelOrganizeCommon'.tr),
 | 
			
		||||
                                leading: const Icon(Icons.tag),
 | 
			
		||||
                                contentPadding:
 | 
			
		||||
                                    const EdgeInsets.symmetric(horizontal: 8),
 | 
			
		||||
                              ),
 | 
			
		||||
                              onTap: () {
 | 
			
		||||
                                AppRouter.instance
 | 
			
		||||
                                    .pushNamed('channelOrganizing')
 | 
			
		||||
                                    .then(
 | 
			
		||||
                                  (value) {
 | 
			
		||||
                                    if (value != null) getChannels();
 | 
			
		||||
                                  },
 | 
			
		||||
                                );
 | 
			
		||||
          return RefreshIndicator(
 | 
			
		||||
            onRefresh: () => getChannels(),
 | 
			
		||||
            child: CustomScrollView(
 | 
			
		||||
              slivers: [
 | 
			
		||||
                SliverAppBar(
 | 
			
		||||
                  title: Text('contact'.tr),
 | 
			
		||||
                  centerTitle: false,
 | 
			
		||||
                  titleSpacing: SolianTheme.isLargeScreen(context) ? null : 24,
 | 
			
		||||
                  actions: [
 | 
			
		||||
                    const NotificationButton(),
 | 
			
		||||
                    PopupMenuButton(
 | 
			
		||||
                      icon: const Icon(Icons.add_circle),
 | 
			
		||||
                      itemBuilder: (BuildContext context) => [
 | 
			
		||||
                        PopupMenuItem(
 | 
			
		||||
                          child: ListTile(
 | 
			
		||||
                            title: Text('channelOrganizeCommon'.tr),
 | 
			
		||||
                            leading: const Icon(Icons.tag),
 | 
			
		||||
                            contentPadding:
 | 
			
		||||
                                const EdgeInsets.symmetric(horizontal: 8),
 | 
			
		||||
                          ),
 | 
			
		||||
                          onTap: () {
 | 
			
		||||
                            AppRouter.instance
 | 
			
		||||
                                .pushNamed('channelOrganizing')
 | 
			
		||||
                                .then(
 | 
			
		||||
                              (value) {
 | 
			
		||||
                                if (value != null) getChannels();
 | 
			
		||||
                              },
 | 
			
		||||
                            ),
 | 
			
		||||
                            PopupMenuItem(
 | 
			
		||||
                              child: ListTile(
 | 
			
		||||
                                title: Text('channelOrganizeDirect'.tr),
 | 
			
		||||
                                leading: const FaIcon(
 | 
			
		||||
                                  FontAwesomeIcons.userGroup,
 | 
			
		||||
                                  size: 16,
 | 
			
		||||
                                ),
 | 
			
		||||
                                contentPadding:
 | 
			
		||||
                                    const EdgeInsets.symmetric(horizontal: 8),
 | 
			
		||||
                              ),
 | 
			
		||||
                              onTap: () {
 | 
			
		||||
                                final ChannelProvider provider = Get.find();
 | 
			
		||||
                                provider
 | 
			
		||||
                                    .createDirectChannel(context, 'global')
 | 
			
		||||
                                    .then((resp) {
 | 
			
		||||
                                  if (resp != null) {
 | 
			
		||||
                                    getChannels();
 | 
			
		||||
                                  }
 | 
			
		||||
                                });
 | 
			
		||||
                              },
 | 
			
		||||
                            ),
 | 
			
		||||
                          ],
 | 
			
		||||
                            );
 | 
			
		||||
                          },
 | 
			
		||||
                        ),
 | 
			
		||||
                        SizedBox(
 | 
			
		||||
                          width: SolianTheme.isLargeScreen(context) ? 8 : 16,
 | 
			
		||||
                        PopupMenuItem(
 | 
			
		||||
                          child: ListTile(
 | 
			
		||||
                            title: Text('channelOrganizeDirect'.tr),
 | 
			
		||||
                            leading: const FaIcon(
 | 
			
		||||
                              FontAwesomeIcons.userGroup,
 | 
			
		||||
                              size: 16,
 | 
			
		||||
                            ),
 | 
			
		||||
                            contentPadding:
 | 
			
		||||
                                const EdgeInsets.symmetric(horizontal: 8),
 | 
			
		||||
                          ),
 | 
			
		||||
                          onTap: () {
 | 
			
		||||
                            final ChannelProvider provider = Get.find();
 | 
			
		||||
                            provider
 | 
			
		||||
                                .createDirectChannel(context, 'global')
 | 
			
		||||
                                .then((resp) {
 | 
			
		||||
                              if (resp != null) {
 | 
			
		||||
                                getChannels();
 | 
			
		||||
                              }
 | 
			
		||||
                            });
 | 
			
		||||
                          },
 | 
			
		||||
                        ),
 | 
			
		||||
                      ],
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ];
 | 
			
		||||
              },
 | 
			
		||||
              body: MediaQuery.removePadding(
 | 
			
		||||
                removeTop: true,
 | 
			
		||||
                context: context,
 | 
			
		||||
                child: Column(
 | 
			
		||||
                  children: [
 | 
			
		||||
                    if (_isBusy)
 | 
			
		||||
                      const LinearProgressIndicator().animate().scaleX(),
 | 
			
		||||
                    Expanded(
 | 
			
		||||
                      child: RefreshIndicator(
 | 
			
		||||
                        onRefresh: () => getChannels(),
 | 
			
		||||
                        child: ListView.builder(
 | 
			
		||||
                          itemCount: _channels.length,
 | 
			
		||||
                          itemBuilder: (context, index) {
 | 
			
		||||
                            final element = _channels[index];
 | 
			
		||||
                            return buildItem(element);
 | 
			
		||||
                          },
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    SizedBox(
 | 
			
		||||
                      width: SolianTheme.isLargeScreen(context) ? 8 : 16,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
                if (_isBusy)
 | 
			
		||||
                  SliverToBoxAdapter(
 | 
			
		||||
                    child: const LinearProgressIndicator().animate().scaleX(),
 | 
			
		||||
                  ),
 | 
			
		||||
                SliverList.builder(
 | 
			
		||||
                  itemCount: _channels.length,
 | 
			
		||||
                  itemBuilder: (context, index) {
 | 
			
		||||
                    final element = _channels[index];
 | 
			
		||||
                    return buildItem(element);
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -44,22 +44,25 @@ class _PostDetailScreenState extends State<PostDetailScreen> {
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          return ListView(
 | 
			
		||||
            children: [
 | 
			
		||||
              PostItem(
 | 
			
		||||
                item: item!,
 | 
			
		||||
                isClickable: true,
 | 
			
		||||
                isShowReply: false,
 | 
			
		||||
          return CustomScrollView(
 | 
			
		||||
            slivers: [
 | 
			
		||||
              SliverToBoxAdapter(
 | 
			
		||||
                child: PostItem(
 | 
			
		||||
                  item: item!,
 | 
			
		||||
                  isClickable: true,
 | 
			
		||||
                  isShowReply: false,
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
              const Divider(thickness: 0.3, height: 0.3),
 | 
			
		||||
              Text(
 | 
			
		||||
                'postReplies'.tr,
 | 
			
		||||
                style: Theme.of(context).textTheme.headlineSmall,
 | 
			
		||||
              ).paddingOnly(left: 24, right: 24, top: 16),
 | 
			
		||||
              PostReplyList(
 | 
			
		||||
                item: item!,
 | 
			
		||||
                shrinkWrap: true,
 | 
			
		||||
              const SliverToBoxAdapter(
 | 
			
		||||
                child: Divider(thickness: 0.3, height: 0.3),
 | 
			
		||||
              ),
 | 
			
		||||
              SliverToBoxAdapter(
 | 
			
		||||
                child: Text(
 | 
			
		||||
                  'postReplies'.tr,
 | 
			
		||||
                  style: Theme.of(context).textTheme.headlineSmall,
 | 
			
		||||
                ).paddingOnly(left: 24, right: 24, top: 16),
 | 
			
		||||
              ),
 | 
			
		||||
              PostReplyList(item: item!),
 | 
			
		||||
            ],
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -66,63 +66,43 @@ class _RealmListScreenState extends State<RealmListScreen> {
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          return SafeArea(
 | 
			
		||||
            child: NestedScrollView(
 | 
			
		||||
              headerSliverBuilder: (context, innerBoxIsScrolled) {
 | 
			
		||||
                return [
 | 
			
		||||
                  SliverOverlapAbsorber(
 | 
			
		||||
                    handle: NestedScrollView.sliverOverlapAbsorberHandleFor(
 | 
			
		||||
                        context),
 | 
			
		||||
                    sliver: SliverAppBar(
 | 
			
		||||
                      title: Text('realm'.tr),
 | 
			
		||||
                      centerTitle: false,
 | 
			
		||||
                      titleSpacing:
 | 
			
		||||
                          SolianTheme.isLargeScreen(context) ? null : 24,
 | 
			
		||||
                      forceElevated: innerBoxIsScrolled,
 | 
			
		||||
                      actions: [
 | 
			
		||||
                        const NotificationButton(),
 | 
			
		||||
                        IconButton(
 | 
			
		||||
                          icon: const Icon(Icons.add_circle),
 | 
			
		||||
                          onPressed: () {
 | 
			
		||||
                            AppRouter.instance
 | 
			
		||||
                                .pushNamed('realmOrganizing')
 | 
			
		||||
                                .then(
 | 
			
		||||
                              (value) {
 | 
			
		||||
                                if (value != null) getRealms();
 | 
			
		||||
                              },
 | 
			
		||||
                            );
 | 
			
		||||
          return RefreshIndicator(
 | 
			
		||||
            onRefresh: () => getRealms(),
 | 
			
		||||
            child: CustomScrollView(
 | 
			
		||||
              slivers: [
 | 
			
		||||
                SliverAppBar(
 | 
			
		||||
                  title: Text('realm'.tr),
 | 
			
		||||
                  centerTitle: false,
 | 
			
		||||
                  titleSpacing: SolianTheme.isLargeScreen(context) ? null : 24,
 | 
			
		||||
                  actions: [
 | 
			
		||||
                    const NotificationButton(),
 | 
			
		||||
                    IconButton(
 | 
			
		||||
                      icon: const Icon(Icons.add_circle),
 | 
			
		||||
                      onPressed: () {
 | 
			
		||||
                        AppRouter.instance.pushNamed('realmOrganizing').then(
 | 
			
		||||
                          (value) {
 | 
			
		||||
                            if (value != null) getRealms();
 | 
			
		||||
                          },
 | 
			
		||||
                        ),
 | 
			
		||||
                        SizedBox(
 | 
			
		||||
                          width: SolianTheme.isLargeScreen(context) ? 8 : 16,
 | 
			
		||||
                        ),
 | 
			
		||||
                      ],
 | 
			
		||||
                        );
 | 
			
		||||
                      },
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ];
 | 
			
		||||
              },
 | 
			
		||||
              body: MediaQuery.removePadding(
 | 
			
		||||
                removeTop: true,
 | 
			
		||||
                context: context,
 | 
			
		||||
                child: Column(
 | 
			
		||||
                  children: [
 | 
			
		||||
                    if (_isBusy)
 | 
			
		||||
                      const LinearProgressIndicator().animate().scaleX(),
 | 
			
		||||
                    Expanded(
 | 
			
		||||
                      child: RefreshIndicator(
 | 
			
		||||
                        onRefresh: () => getRealms(),
 | 
			
		||||
                        child: ListView.builder(
 | 
			
		||||
                          itemCount: _realms.length,
 | 
			
		||||
                          itemBuilder: (context, index) {
 | 
			
		||||
                            final element = _realms[index];
 | 
			
		||||
                            return buildRealm(element);
 | 
			
		||||
                          },
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    SizedBox(
 | 
			
		||||
                      width: SolianTheme.isLargeScreen(context) ? 8 : 16,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ],
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
                if (_isBusy)
 | 
			
		||||
                  SliverToBoxAdapter(
 | 
			
		||||
                    child: const LinearProgressIndicator().animate().scaleX(),
 | 
			
		||||
                  ),
 | 
			
		||||
                SliverList.builder(
 | 
			
		||||
                  itemCount: _realms.length,
 | 
			
		||||
                  itemBuilder: (context, index) {
 | 
			
		||||
                    final element = _realms[index];
 | 
			
		||||
                    return buildRealm(element);
 | 
			
		||||
                  },
 | 
			
		||||
                )
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -72,39 +72,24 @@ class _SocialScreenState extends State<SocialScreen> {
 | 
			
		||||
          }),
 | 
			
		||||
      body: Material(
 | 
			
		||||
        color: Theme.of(context).colorScheme.surface,
 | 
			
		||||
        child: SafeArea(
 | 
			
		||||
          child: NestedScrollView(
 | 
			
		||||
            headerSliverBuilder: (context, innerBoxIsScrolled) {
 | 
			
		||||
              return [
 | 
			
		||||
                SliverOverlapAbsorber(
 | 
			
		||||
                  handle:
 | 
			
		||||
                      NestedScrollView.sliverOverlapAbsorberHandleFor(context),
 | 
			
		||||
                  sliver: SliverAppBar(
 | 
			
		||||
                    title: Text('social'.tr),
 | 
			
		||||
                    centerTitle: false,
 | 
			
		||||
                    floating: true,
 | 
			
		||||
                    snap: true,
 | 
			
		||||
                    titleSpacing:
 | 
			
		||||
                        SolianTheme.isLargeScreen(context) ? null : 24,
 | 
			
		||||
                    forceElevated: innerBoxIsScrolled,
 | 
			
		||||
                    actions: [
 | 
			
		||||
                      const NotificationButton(),
 | 
			
		||||
                      SizedBox(
 | 
			
		||||
                        width: SolianTheme.isLargeScreen(context) ? 8 : 16,
 | 
			
		||||
                      ),
 | 
			
		||||
                    ],
 | 
			
		||||
        child: RefreshIndicator(
 | 
			
		||||
          onRefresh: () => Future.sync(() => _pagingController.refresh()),
 | 
			
		||||
          child: CustomScrollView(
 | 
			
		||||
            slivers: [
 | 
			
		||||
              SliverAppBar(
 | 
			
		||||
                title: Text('social'.tr),
 | 
			
		||||
                centerTitle: false,
 | 
			
		||||
                floating: true,
 | 
			
		||||
                titleSpacing: SolianTheme.isLargeScreen(context) ? null : 24,
 | 
			
		||||
                actions: [
 | 
			
		||||
                  const NotificationButton(),
 | 
			
		||||
                  SizedBox(
 | 
			
		||||
                    width: SolianTheme.isLargeScreen(context) ? 8 : 16,
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ];
 | 
			
		||||
            },
 | 
			
		||||
            body: MediaQuery.removePadding(
 | 
			
		||||
              removeTop: true,
 | 
			
		||||
              context: context,
 | 
			
		||||
              child: RefreshIndicator(
 | 
			
		||||
                onRefresh: () => Future.sync(() => _pagingController.refresh()),
 | 
			
		||||
                child: PostListWidget(controller: _pagingController),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
              PostListWidget(controller: _pagingController),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,6 @@ import 'package:solian/widgets/posts/post_action.dart';
 | 
			
		||||
import 'package:solian/widgets/posts/post_item.dart';
 | 
			
		||||
 | 
			
		||||
class PostListWidget extends StatelessWidget {
 | 
			
		||||
  final bool shrinkWrap;
 | 
			
		||||
  final bool isShowEmbed;
 | 
			
		||||
  final bool isClickable;
 | 
			
		||||
  final bool isNestedClickable;
 | 
			
		||||
@@ -16,7 +15,6 @@ class PostListWidget extends StatelessWidget {
 | 
			
		||||
  const PostListWidget({
 | 
			
		||||
    super.key,
 | 
			
		||||
    required this.controller,
 | 
			
		||||
    this.shrinkWrap = false,
 | 
			
		||||
    this.isShowEmbed = true,
 | 
			
		||||
    this.isClickable = true,
 | 
			
		||||
    this.isNestedClickable = true,
 | 
			
		||||
@@ -24,8 +22,7 @@ class PostListWidget extends StatelessWidget {
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return PagedListView<int, Post>.separated(
 | 
			
		||||
      shrinkWrap: shrinkWrap,
 | 
			
		||||
    return PagedSliverList<int, Post>.separated(
 | 
			
		||||
      pagingController: controller,
 | 
			
		||||
      builderDelegate: PagedChildBuilderDelegate<Post>(
 | 
			
		||||
        itemBuilder: (context, item, index) {
 | 
			
		||||
 
 | 
			
		||||
@@ -8,12 +8,10 @@ import 'package:solian/widgets/posts/post_list.dart';
 | 
			
		||||
 | 
			
		||||
class PostReplyList extends StatefulWidget {
 | 
			
		||||
  final Post item;
 | 
			
		||||
  final bool shrinkWrap;
 | 
			
		||||
 | 
			
		||||
  const PostReplyList({
 | 
			
		||||
    super.key,
 | 
			
		||||
    required this.item,
 | 
			
		||||
    this.shrinkWrap = false,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
@@ -55,7 +53,6 @@ class _PostReplyListState extends State<PostReplyList> {
 | 
			
		||||
  Widget build(BuildContext context) {
 | 
			
		||||
    return PostListWidget(
 | 
			
		||||
      isShowEmbed: false,
 | 
			
		||||
      shrinkWrap: widget.shrinkWrap,
 | 
			
		||||
      controller: _pagingController,
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
@@ -76,8 +73,8 @@ class PostReplyListPopup extends StatelessWidget {
 | 
			
		||||
          style: Theme.of(context).textTheme.headlineSmall,
 | 
			
		||||
        ).paddingOnly(left: 24, right: 24, top: 32, bottom: 16),
 | 
			
		||||
        Expanded(
 | 
			
		||||
          child: PostReplyList(
 | 
			
		||||
            item: item,
 | 
			
		||||
          child: CustomScrollView(
 | 
			
		||||
            slivers: [PostReplyList(item: item)],
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user