⚡ Improve post detail page first time loading
This commit is contained in:
		@@ -2,6 +2,7 @@ import 'package:animations/animations.dart';
 | 
				
			|||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
import 'package:go_router/go_router.dart';
 | 
					import 'package:go_router/go_router.dart';
 | 
				
			||||||
import 'package:solian/bootstrapper.dart';
 | 
					import 'package:solian/bootstrapper.dart';
 | 
				
			||||||
 | 
					import 'package:solian/models/post.dart';
 | 
				
			||||||
import 'package:solian/models/realm.dart';
 | 
					import 'package:solian/models/realm.dart';
 | 
				
			||||||
import 'package:solian/screens/about.dart';
 | 
					import 'package:solian/screens/about.dart';
 | 
				
			||||||
import 'package:solian/screens/account.dart';
 | 
					import 'package:solian/screens/account.dart';
 | 
				
			||||||
@@ -108,6 +109,7 @@ abstract class AppRouter {
 | 
				
			|||||||
          state: state,
 | 
					          state: state,
 | 
				
			||||||
          child: PostDetailScreen(
 | 
					          child: PostDetailScreen(
 | 
				
			||||||
            id: state.pathParameters['id']!,
 | 
					            id: state.pathParameters['id']!,
 | 
				
			||||||
 | 
					            post: state.extra as Post?,
 | 
				
			||||||
          ),
 | 
					          ),
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
      ),
 | 
					      ),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,7 @@ import 'package:solian/widgets/app_bar_title.dart';
 | 
				
			|||||||
import 'package:solian/widgets/channel/channel_list.dart';
 | 
					import 'package:solian/widgets/channel/channel_list.dart';
 | 
				
			||||||
import 'package:solian/widgets/chat/call/chat_call_indicator.dart';
 | 
					import 'package:solian/widgets/chat/call/chat_call_indicator.dart';
 | 
				
			||||||
import 'package:solian/widgets/current_state_action.dart';
 | 
					import 'package:solian/widgets/current_state_action.dart';
 | 
				
			||||||
 | 
					import 'package:solian/widgets/loading_indicator.dart';
 | 
				
			||||||
import 'package:solian/widgets/root_container.dart';
 | 
					import 'package:solian/widgets/root_container.dart';
 | 
				
			||||||
import 'package:solian/widgets/sidebar/empty_placeholder.dart';
 | 
					import 'package:solian/widgets/sidebar/empty_placeholder.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -280,26 +281,7 @@ class _ChatListState extends State<ChatList> {
 | 
				
			|||||||
              return Column(
 | 
					              return Column(
 | 
				
			||||||
                children: [
 | 
					                children: [
 | 
				
			||||||
                  const ChatCallCurrentIndicator(),
 | 
					                  const ChatCallCurrentIndicator(),
 | 
				
			||||||
                  if (_isBusy)
 | 
					                  if (_isBusy) const LoadingIndicator(),
 | 
				
			||||||
                    Container(
 | 
					 | 
				
			||||||
                      color: Theme.of(context)
 | 
					 | 
				
			||||||
                          .colorScheme
 | 
					 | 
				
			||||||
                          .surfaceContainerLow
 | 
					 | 
				
			||||||
                          .withOpacity(0.8),
 | 
					 | 
				
			||||||
                      child: Row(
 | 
					 | 
				
			||||||
                        mainAxisAlignment: MainAxisAlignment.center,
 | 
					 | 
				
			||||||
                        crossAxisAlignment: CrossAxisAlignment.center,
 | 
					 | 
				
			||||||
                        children: [
 | 
					 | 
				
			||||||
                          const SizedBox(
 | 
					 | 
				
			||||||
                            height: 16,
 | 
					 | 
				
			||||||
                            width: 16,
 | 
					 | 
				
			||||||
                            child: CircularProgressIndicator(strokeWidth: 2.5),
 | 
					 | 
				
			||||||
                          ),
 | 
					 | 
				
			||||||
                          const Gap(8),
 | 
					 | 
				
			||||||
                          Text('loading'.tr)
 | 
					 | 
				
			||||||
                        ],
 | 
					 | 
				
			||||||
                      ).paddingSymmetric(vertical: 8),
 | 
					 | 
				
			||||||
                    ),
 | 
					 | 
				
			||||||
                  Expanded(
 | 
					                  Expanded(
 | 
				
			||||||
                    child: TabBarView(
 | 
					                    child: TabBarView(
 | 
				
			||||||
                      children: [
 | 
					                      children: [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ import 'package:solian/models/post.dart';
 | 
				
			|||||||
import 'package:solian/providers/content/posts.dart';
 | 
					import 'package:solian/providers/content/posts.dart';
 | 
				
			||||||
import 'package:solian/providers/last_read.dart';
 | 
					import 'package:solian/providers/last_read.dart';
 | 
				
			||||||
import 'package:solian/theme.dart';
 | 
					import 'package:solian/theme.dart';
 | 
				
			||||||
 | 
					import 'package:solian/widgets/loading_indicator.dart';
 | 
				
			||||||
import 'package:solian/widgets/posts/post_item.dart';
 | 
					import 'package:solian/widgets/posts/post_item.dart';
 | 
				
			||||||
import 'package:solian/widgets/posts/post_replies.dart';
 | 
					import 'package:solian/widgets/posts/post_replies.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -23,87 +24,91 @@ class PostDetailScreen extends StatefulWidget {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _PostDetailScreenState extends State<PostDetailScreen> {
 | 
					class _PostDetailScreenState extends State<PostDetailScreen> {
 | 
				
			||||||
  Post? item;
 | 
					  bool _isBusy = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Future<Post?> _getDetail() async {
 | 
					  Post? _item;
 | 
				
			||||||
    if (widget.post != null) {
 | 
					 | 
				
			||||||
      setState(() {
 | 
					 | 
				
			||||||
        item = widget.post;
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final PostProvider provider = Get.find();
 | 
					  Future<void> _getDetail() async {
 | 
				
			||||||
 | 
					    final PostProvider posts = Get.find();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      final resp = await provider.getPost(widget.id);
 | 
					      final resp = await posts.getPost(widget.id);
 | 
				
			||||||
      item = Post.fromJson(resp.body);
 | 
					      _item = Post.fromJson(resp.body);
 | 
				
			||||||
    } catch (e) {
 | 
					    } catch (e) {
 | 
				
			||||||
      context.showErrorDialog(e).then((_) => Navigator.pop(context));
 | 
					      context.showErrorDialog(e).then((_) => Navigator.pop(context));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Get.find<LastReadProvider>().feedLastReadAt = item?.id;
 | 
					    Get.find<LastReadProvider>().feedLastReadAt = _item?.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return item;
 | 
					    setState(() => _isBusy = false);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void initState() {
 | 
				
			||||||
 | 
					    super.initState();
 | 
				
			||||||
 | 
					    if (widget.post != null) {
 | 
				
			||||||
 | 
					      _item = widget.post;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    _getDetail();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return FutureBuilder(
 | 
					    if (_isBusy && _item == null) {
 | 
				
			||||||
      future: _getDetail(),
 | 
					      return const Center(
 | 
				
			||||||
      builder: (context, snapshot) {
 | 
					        child: CircularProgressIndicator(),
 | 
				
			||||||
        if (!snapshot.hasData || snapshot.data == null) {
 | 
					      );
 | 
				
			||||||
          return const Center(
 | 
					    }
 | 
				
			||||||
            child: CircularProgressIndicator(),
 | 
					 | 
				
			||||||
          );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return CustomScrollView(
 | 
					    return CustomScrollView(
 | 
				
			||||||
          slivers: [
 | 
					      slivers: [
 | 
				
			||||||
            SliverToBoxAdapter(
 | 
					        if (_isBusy)
 | 
				
			||||||
              child: PostItem(
 | 
					          SliverToBoxAdapter(
 | 
				
			||||||
                item: item!,
 | 
					            child: LoadingIndicator(),
 | 
				
			||||||
                isClickable: false,
 | 
					          ),
 | 
				
			||||||
                isOverrideEmbedClickable: true,
 | 
					        SliverToBoxAdapter(
 | 
				
			||||||
                isFullDate: true,
 | 
					          child: PostItem(
 | 
				
			||||||
                isShowReply: false,
 | 
					            item: _item!,
 | 
				
			||||||
                isContentSelectable: true,
 | 
					            isClickable: false,
 | 
				
			||||||
                padding: AppTheme.isLargeScreen(context)
 | 
					            isOverrideEmbedClickable: true,
 | 
				
			||||||
                    ? EdgeInsets.symmetric(
 | 
					            isFullDate: true,
 | 
				
			||||||
                        horizontal: 4,
 | 
					            isShowReply: false,
 | 
				
			||||||
                        vertical: 8,
 | 
					            isContentSelectable: true,
 | 
				
			||||||
                      )
 | 
					            padding: AppTheme.isLargeScreen(context)
 | 
				
			||||||
                    : EdgeInsets.zero,
 | 
					                ? EdgeInsets.symmetric(
 | 
				
			||||||
              ),
 | 
					                    horizontal: 4,
 | 
				
			||||||
            ),
 | 
					                    vertical: 8,
 | 
				
			||||||
            SliverToBoxAdapter(
 | 
					                  )
 | 
				
			||||||
              child: const Divider(thickness: 0.3, height: 1).paddingOnly(
 | 
					                : EdgeInsets.zero,
 | 
				
			||||||
                top: 8,
 | 
					          ),
 | 
				
			||||||
              ),
 | 
					        ),
 | 
				
			||||||
            ),
 | 
					        SliverToBoxAdapter(
 | 
				
			||||||
            SliverToBoxAdapter(
 | 
					          child: const Divider(thickness: 0.3, height: 1).paddingOnly(
 | 
				
			||||||
              child: Align(
 | 
					            top: 8,
 | 
				
			||||||
                alignment: Alignment.centerLeft,
 | 
					          ),
 | 
				
			||||||
                child: Text(
 | 
					        ),
 | 
				
			||||||
                  'postReplies'.tr,
 | 
					        SliverToBoxAdapter(
 | 
				
			||||||
                  style: Theme.of(context).textTheme.headlineSmall,
 | 
					          child: Align(
 | 
				
			||||||
                ).paddingOnly(left: 24, right: 24, top: 16),
 | 
					            alignment: Alignment.centerLeft,
 | 
				
			||||||
              ),
 | 
					            child: Text(
 | 
				
			||||||
            ),
 | 
					              'postReplies'.tr,
 | 
				
			||||||
            PostReplyList(
 | 
					              style: Theme.of(context).textTheme.headlineSmall,
 | 
				
			||||||
              item: item!,
 | 
					            ).paddingOnly(left: 24, right: 24, top: 16),
 | 
				
			||||||
              padding: AppTheme.isLargeScreen(context)
 | 
					          ),
 | 
				
			||||||
                  ? EdgeInsets.symmetric(
 | 
					        ),
 | 
				
			||||||
                      horizontal: 4,
 | 
					        PostReplyList(
 | 
				
			||||||
                      vertical: 8,
 | 
					          item: _item!,
 | 
				
			||||||
                    )
 | 
					          padding: AppTheme.isLargeScreen(context)
 | 
				
			||||||
                  : EdgeInsets.zero,
 | 
					              ? EdgeInsets.symmetric(
 | 
				
			||||||
            ),
 | 
					                  horizontal: 4,
 | 
				
			||||||
            SliverToBoxAdapter(
 | 
					                  vertical: 8,
 | 
				
			||||||
              child: SizedBox(height: MediaQuery.of(context).padding.bottom),
 | 
					                )
 | 
				
			||||||
            ),
 | 
					              : EdgeInsets.zero,
 | 
				
			||||||
          ],
 | 
					        ),
 | 
				
			||||||
        );
 | 
					        SliverToBoxAdapter(
 | 
				
			||||||
      },
 | 
					          child: SizedBox(height: MediaQuery.of(context).padding.bottom),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      ],
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								lib/widgets/loading_indicator.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								lib/widgets/loading_indicator.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					import 'package:gap/gap.dart';
 | 
				
			||||||
 | 
					import 'package:get/get.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LoadingIndicator extends StatelessWidget {
 | 
				
			||||||
 | 
					  const LoadingIndicator({super.key});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    return Container(
 | 
				
			||||||
 | 
					      padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24),
 | 
				
			||||||
 | 
					      color: Theme.of(context).colorScheme.surfaceContainerLow.withOpacity(0.8),
 | 
				
			||||||
 | 
					      child: Row(
 | 
				
			||||||
 | 
					        mainAxisAlignment: MainAxisAlignment.center,
 | 
				
			||||||
 | 
					        crossAxisAlignment: CrossAxisAlignment.center,
 | 
				
			||||||
 | 
					        children: [
 | 
				
			||||||
 | 
					          const SizedBox(
 | 
				
			||||||
 | 
					            height: 16,
 | 
				
			||||||
 | 
					            width: 16,
 | 
				
			||||||
 | 
					            child: CircularProgressIndicator(strokeWidth: 2.5),
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          const Gap(8),
 | 
				
			||||||
 | 
					          Text('loading'.tr)
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -250,6 +250,7 @@ class _PostItemState extends State<PostItem> {
 | 
				
			|||||||
          AppRouter.instance.pushNamed(
 | 
					          AppRouter.instance.pushNamed(
 | 
				
			||||||
            'postDetail',
 | 
					            'postDetail',
 | 
				
			||||||
            pathParameters: {'id': item.id.toString()},
 | 
					            pathParameters: {'id': item.id.toString()},
 | 
				
			||||||
 | 
					            extra: item,
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user