Fliin
On this page

Screens / lists

Social feed

Shows the posts of followed people with their photos, optimistic likes, comments and share, hide or report with undo, older posts on scroll and new posts on demand without moving the reading.

16 of 16 variants pass
Social feed, loaded state
Platform
Brightness
Text scale
Direction

Import

Social feed is part of Fliin Pro. With a license key, sign in once with fliin login, then add it in a Flutter app where fliin init has run:

fliin login
fliin add feed

Also installs: alert, app-bar, button, core, empty-state, link-button, menu, page, post-card, result-card, skeleton, spinner, text, avatar, icons

Examples

Loaded

The feed loaded: posts, more of them loaded while scrolling down. standalone holds its own controller over the service you pass; give it yours to reach your backend.

Social feed, loaded state
import 'package:your_app/ui/fliin/screens/feed/feed_controller.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_data.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_screen.dart';
import 'package:flutter/widgets.dart';

class FeedLoadedExample extends StatelessWidget {
  const FeedLoadedExample({super.key});

  @override
  Widget build(BuildContext context) {
    return FeedScreen.standalone(
      service: SampleFeedService(latency: Duration(milliseconds: 600)),
      initial: FeedLoaded(sampleFeedPosts.take(8).toList(), more: const FeedMoreAvailable('8')),
      now: () => sampleFeedNow,
      onOpen: (_) {},
      onComment: (_) {},
      onAuthor: (_) {},
      onShare: (_) {},
    );
  }
}

Incoming

New posts arrived while the user was reading: a pill at the top offers to show them, without moving what is on screen.

Social feed, incoming state
import 'package:your_app/ui/fliin/screens/feed/feed_controller.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_data.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_screen.dart';
import 'package:flutter/widgets.dart';

class FeedIncomingExample extends StatelessWidget {
  const FeedIncomingExample({super.key});

  @override
  Widget build(BuildContext context) {
    return FeedScreen.standalone(
      service: SampleFeedService(latency: Duration(milliseconds: 600)),
      initial: FeedLoaded(
        sampleFeedPosts.take(8).toList(),
        more: const FeedMoreAvailable('8'),
        incoming: [sampleFeedArrival],
      ),
      now: () => sampleFeedNow,
      onOpen: (_) {},
      onComment: (_) {},
      onAuthor: (_) {},
      onShare: (_) {},
    );
  }
}

Hidden

A post the user just hid: it gives way to a line that offers to undo, and the service only hears of it once the screen closes.

Social feed, hidden state
import 'package:your_app/ui/fliin/screens/feed/feed_controller.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_data.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_screen.dart';
import 'package:flutter/widgets.dart';

class FeedHiddenExample extends StatelessWidget {
  const FeedHiddenExample({super.key});

  @override
  Widget build(BuildContext context) {
    return FeedScreen.standalone(
      service: SampleFeedService(latency: Duration.zero),
      initial: FeedLoaded(
        sampleFeedPosts.take(8).toList(),
        more: const FeedMoreAvailable('8'),
        removed: const {'p01': FeedRemoval(FeedRemovalKind.hidden, undoable: true)},
      ),
      now: () => sampleFeedNow,
      onOpen: (_) {},
      onComment: (_) {},
      onAuthor: (_) {},
      onShare: (_) {},
    );
  }
}

Loading

While the service answers, placeholders keep the shape of the posts.

Social feed, loading state
import 'package:your_app/ui/fliin/screens/feed/feed_controller.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_data.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_screen.dart';
import 'package:flutter/widgets.dart';

class FeedLoadingExample extends StatelessWidget {
  const FeedLoadingExample({super.key});

  @override
  Widget build(BuildContext context) {
    return FeedScreen.standalone(
      service: SampleFeedService(latency: Duration(milliseconds: 600)),
      initial: const FeedLoading(),
      now: () => sampleFeedNow,
      onOpen: (_) {},
      onComment: (_) {},
      onAuthor: (_) {},
      onShare: (_) {},
    );
  }
}

Empty

Nothing to show yet: the feed says so instead of staying blank.

Social feed, empty state
import 'package:your_app/ui/fliin/screens/feed/feed_controller.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_data.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_screen.dart';
import 'package:flutter/widgets.dart';

class FeedEmptyExample extends StatelessWidget {
  const FeedEmptyExample({super.key});

  @override
  Widget build(BuildContext context) {
    return FeedScreen.standalone(
      service: SampleFeedService(latency: Duration(milliseconds: 600)),
      initial: const FeedEmpty(),
      now: () => sampleFeedNow,
      onOpen: (_) {},
      onComment: (_) {},
      onAuthor: (_) {},
      onShare: (_) {},
    );
  }
}

Error

When loading fails, the screen says so and offers to try again.

Social feed, error state
import 'package:your_app/ui/fliin/screens/feed/feed_controller.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_data.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_screen.dart';
import 'package:flutter/widgets.dart';

class FeedErrorExample extends StatelessWidget {
  const FeedErrorExample({super.key});

  @override
  Widget build(BuildContext context) {
    return FeedScreen.standalone(
      service: SampleFeedService(latency: Duration(milliseconds: 600)),
      initial: const FeedFailed(),
      now: () => sampleFeedNow,
      onOpen: (_) {},
      onComment: (_) {},
      onAuthor: (_) {},
      onShare: (_) {},
    );
  }
}

Offline

Without a connection, the screen asks to reconnect rather than showing an error.

Social feed, offline state
import 'package:your_app/ui/fliin/screens/feed/feed_controller.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_data.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_screen.dart';
import 'package:flutter/widgets.dart';

class FeedOfflineExample extends StatelessWidget {
  const FeedOfflineExample({super.key});

  @override
  Widget build(BuildContext context) {
    return FeedScreen.standalone(
      service: SampleFeedService(latency: Duration(milliseconds: 600)),
      initial: const FeedOffline(),
      now: () => sampleFeedNow,
      onOpen: (_) {},
      onComment: (_) {},
      onAuthor: (_) {},
      onShare: (_) {},
    );
  }
}

Compose

The feed with a way to post and a head of your own: onCompose puts a pencil in the bar (and an action in the empty feed), header a row of people above the posts.

Social feed, compose state
import 'package:your_app/ui/fliin/components/avatar/fl_avatar.dart';
import 'package:your_app/ui/fliin/components/text/fl_text.dart';
import 'package:your_app/ui/fliin/core/fliin.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_controller.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_data.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_screen.dart';
import 'package:flutter/widgets.dart';

class FeedComposeExample extends StatelessWidget {
  const FeedComposeExample({super.key});

  @override
  Widget build(BuildContext context) {
    final authors = {for (final post in sampleFeedPosts) post.author.id: post.author}.values.toList();
    return FeedScreen.standalone(
      service: SampleFeedService(latency: Duration(milliseconds: 600)),
      initial: FeedLoaded(sampleFeedPosts.take(8).toList(), more: const FeedMoreAvailable('8')),
      now: () => sampleFeedNow,
      onCompose: () {},
      header: _People(authors: authors),
      onOpen: (_) {},
      onComment: (_) {},
      onAuthor: (_) {},
      onShare: (_) {},
    );
  }
}

class _People extends StatelessWidget {
  const _People({required this.authors});

  final List<FeedAuthor> authors;

  @override
  Widget build(BuildContext context) {
    final space = FlScope.of(context).space;
    return SingleChildScrollView(
      scrollDirection: Axis.horizontal,
      padding: EdgeInsetsDirectional.fromSTEB(space.lg, space.sm, space.lg, space.lg),
      child: Row(
        spacing: space.lg,
        children: [
          for (final author in authors)
            Column(
              spacing: space.xs,
              children: [
                FlAvatar(
                  initials: author.initials,
                  image: author.avatar,
                  color: author.color,
                  semanticLabel: author.name,
                  size: FlAvatarSize.lg,
                ),
                ExcludeSemantics(child: FlText(author.name.split(' ').first, type: FlTextType.bodyXs)),
              ],
            ),
        ],
      ),
    );
  }
}

Routing

fliin add feed prints the route for the router it finds in your pubspec, a file next to the page that wires its navigation. Each of them:

go_router

// lib/feed_route.dart, next to feed_page.dart.
import 'package:go_router/go_router.dart';

import 'feed_page.dart';

/// Add feedRoute to the routes of your GoRouter.
final feedRoute = GoRoute(
  path: '/feed',
  builder: (context, state) => FeedScreenPage(
    onOpen: (post) => context.push('/post/${Uri.encodeComponent(post.id)}'),
    onComment: (post) => context.push('/post/${Uri.encodeComponent(post.id)}?compose=true'),
    onAuthor: (author) => context.push('/profile/${Uri.encodeComponent(author.id)}'),
  ),
);

navigator

// lib/feed_route.dart, next to feed_page.dart.
import 'package:flutter/material.dart';

import 'feed_page.dart';
import 'post_route.dart';
import 'profile_route.dart';

/// Opens “Social feed”: `Navigator.of(context).push(feedRoute())`.
Route<void> feedRoute() => MaterialPageRoute<void>(
  builder: (context) => FeedScreenPage(
    onOpen: (post) => Navigator.of(context).push(postRoute(post.id)),
    onComment: (post) => Navigator.of(context).push(postRoute(post.id, compose: true)),
    onAuthor: (author) => Navigator.of(context).push(profileRoute(author.id)),
  ),
);

State

The controller is a ChangeNotifier from widgets.dart, with no package. fliin add feed prints the page that holds it with the state management it finds, its imports written for your package; below they read your_app. Your backend implements the Service interface of the screen's _data.dart file:

none

// lib/feed_page.dart
// Your backend: implement FeedService (feed_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/feed/feed_data.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_screen.dart';
import 'package:flutter/widgets.dart';

class FeedScreenPage extends StatelessWidget {
  const FeedScreenPage({
    super.key,
    required this.onOpen,
    required this.onComment,
    required this.onAuthor,
  });

  final ValueChanged<FeedPost> onOpen;
  final ValueChanged<FeedPost> onComment;
  final ValueChanged<FeedAuthor> onAuthor;

  @override
  Widget build(BuildContext context) => FeedScreen.standalone(
    service: SampleFeedService(),
    onOpen: onOpen,
    onComment: onComment,
    onAuthor: onAuthor,
  );
}

provider

// lib/feed_page.dart
// Your backend: implement FeedService (feed_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/feed/feed_controller.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_data.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';

class FeedScreenPage extends StatelessWidget {
  const FeedScreenPage({
    super.key,
    required this.onOpen,
    required this.onComment,
    required this.onAuthor,
  });

  final ValueChanged<FeedPost> onOpen;
  final ValueChanged<FeedPost> onComment;
  final ValueChanged<FeedAuthor> onAuthor;

  @override
  Widget build(BuildContext context) => ChangeNotifierProvider(
    create: (_) => FeedController(service: SampleFeedService())..load(),
    child: Builder(
      builder: (context) => FeedScreen(
        controller: context.read<FeedController>(),
        onOpen: onOpen,
        onComment: onComment,
        onAuthor: onAuthor,
      ),
    ),
  );
}

riverpod

// lib/feed_page.dart
// Your backend: implement FeedService (feed_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/feed/feed_controller.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_data.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

final feedControllerProvider = Provider.autoDispose<FeedController>((ref) {
  final controller = FeedController(service: SampleFeedService())..load();
  ref.onDispose(controller.dispose);
  return controller;
});

class FeedScreenPage extends ConsumerWidget {
  const FeedScreenPage({
    super.key,
    required this.onOpen,
    required this.onComment,
    required this.onAuthor,
  });

  final ValueChanged<FeedPost> onOpen;
  final ValueChanged<FeedPost> onComment;
  final ValueChanged<FeedAuthor> onAuthor;

  @override
  Widget build(BuildContext context, WidgetRef ref) => FeedScreen(
    controller: ref.watch(feedControllerProvider),
    onOpen: onOpen,
    onComment: onComment,
    onAuthor: onAuthor,
  );
}

bloc

// lib/feed_page.dart
// Your backend: implement FeedService (feed_data.dart) and pass it instead of the sample service.
import 'package:your_app/ui/fliin/screens/feed/feed_controller.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_data.dart';
import 'package:your_app/ui/fliin/screens/feed/feed_screen.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

/// The screen's controller as a bloc, for your BlocListener and BlocBuilder:
/// one emission per controller notification. `version` tells them apart
/// when `state` does not change (mode, filters, text…, read them on
/// `controller`). The screen listens to the controller itself.
class FeedCubit extends Cubit<({FeedState state, int version})> {
  FeedCubit(this.controller) : super((state: controller.state, version: 0)) {
    controller.addListener(_changed);
  }

  final FeedController controller;

  void _changed() => emit((state: controller.state, version: state.version + 1));

  @override
  Future<void> close() {
    controller
      ..removeListener(_changed)
      ..dispose();
    return super.close();
  }
}

class FeedScreenPage extends StatelessWidget {
  const FeedScreenPage({
    super.key,
    required this.onOpen,
    required this.onComment,
    required this.onAuthor,
  });

  final ValueChanged<FeedPost> onOpen;
  final ValueChanged<FeedPost> onComment;
  final ValueChanged<FeedAuthor> onAuthor;

  @override
  Widget build(BuildContext context) => BlocProvider(
    create: (_) => FeedCubit(FeedController(service: SampleFeedService())..load()),
    child: Builder(
      builder: (context) => FeedScreen(
        controller: context.read<FeedCubit>().controller,
        onOpen: onOpen,
        onComment: onComment,
        onAuthor: onAuthor,
      ),
    ),
  );
}

The snippets lead to post, profile: install these screens too and paste their snippets.

API

FeedScreen: class extends StatefulWidget
FeedScreen.new: const new({super.key, required FeedController controller, ValueChanged<FeedPost>? onOpen, ValueChanged<FeedPost>? onComment, ValueChanged<FeedAuthor>? onAuthor, ValueChanged<FeedPost>? onShare, DateTime Function() now, VoidCallback? onCompose, Widget? header})
FeedScreen.standalone: const new({super.key, required FeedService service, FeedState? initial, ValueChanged<FeedPost>? onOpen, ValueChanged<FeedPost>? onComment, ValueChanged<FeedAuthor>? onAuthor, ValueChanged<FeedPost>? onShare, DateTime Function() now, VoidCallback? onCompose, Widget? header})
FeedScreen.controller: get FeedController?
FeedScreen.service: get FeedService?
FeedScreen.initial: get FeedState?
FeedScreen.onOpen: get ValueChanged<FeedPost>?
FeedScreen.onComment: get ValueChanged<FeedPost>?
FeedScreen.onAuthor: get ValueChanged<FeedAuthor>?
FeedScreen.onShare: get ValueChanged<FeedPost>?
FeedScreen.now: get DateTime Function()
FeedScreen.onCompose: get VoidCallback?
FeedScreen.header: get Widget?
FeedScreen.createState: State<FeedScreen>()
FeedState: sealed class permits {FeedEmpty, FeedFailed, FeedLoaded, FeedLoading, FeedOffline}
FeedState.new: const new()
FeedLoading: final class extends FeedState
FeedLoading.new: const new()
FeedFailed: final class extends FeedState
FeedFailed.new: const new()
FeedOffline: final class extends FeedState
FeedOffline.new: const new()
FeedMore: sealed class permits {FeedMoreAvailable, FeedMoreEnd, FeedMoreFailed, FeedMoreLoading}
FeedMore.new: const new()
FeedMoreAvailable: final class extends FeedMore
FeedMoreAvailable.new: const new(String after)
FeedMoreAvailable.after: get String
FeedMoreLoading: final class extends FeedMore
FeedMoreLoading.new: const new(String after)
FeedMoreLoading.after: get String
FeedMoreFailed: final class extends FeedMore
FeedMoreFailed.new: const new(String after, {required bool offline})
FeedMoreFailed.after: get String
FeedMoreFailed.offline: get bool
FeedMoreEnd: final class extends FeedMore
FeedMoreEnd.new: const new()
FeedRemovalKind: enum {hidden, reported}
FeedRemovalKind.hidden: static final FeedRemovalKind
FeedRemovalKind.reported: static final FeedRemovalKind
FeedRemoval: final class
FeedRemoval.new: const new(FeedRemovalKind kind, {required bool undoable})
FeedRemoval.kind: get FeedRemovalKind
FeedRemoval.undoable: get bool
FeedNotice: enum {likeFailed, hideFailed, reportFailed, refreshFailed, refreshOffline}
FeedNotice.likeFailed: static final FeedNotice
FeedNotice.hideFailed: static final FeedNotice
FeedNotice.reportFailed: static final FeedNotice
FeedNotice.refreshFailed: static final FeedNotice
FeedNotice.refreshOffline: static final FeedNotice
FeedLoaded: final class extends FeedState
FeedLoaded.new: new(List<FeedPost> posts, {FeedMore more, Map<String, FeedRemoval> removed, List<FeedPost> incoming, FeedNotice? notice})
FeedLoaded.posts: get List<FeedPost>
FeedLoaded.more: get FeedMore
FeedLoaded.removed: get Map<String, FeedRemoval>
FeedLoaded.incoming: get List<FeedPost>
FeedLoaded.notice: get FeedNotice?
FeedEmpty: final class extends FeedState
FeedEmpty.new: const new({FeedNotice? notice})
FeedEmpty.notice: get FeedNotice?
FeedController: class extends ChangeNotifier
FeedController.new: new({required FeedService service, FeedState initial, Duration undoDelay})
FeedController.service: get FeedService
FeedController.undoDelay: get Duration
FeedController.state: get FeedState
FeedController.load: Future<void>()
FeedController.refresh: Future<void>()
FeedController.loadMore: Future<bool>()
FeedController.showIncoming: bool()
FeedController.toggleLike: bool(String postId)
FeedController.hide: bool(String postId)
FeedController.report: bool(String postId)
FeedController.undo: bool(String postId)
FeedController.dispose: void()
FeedAuthor: final class
FeedAuthor.new: const new({required String id, required String name, required String initials, ImageProvider? avatar, FlAvatarColor color})
FeedAuthor.id: get String
FeedAuthor.name: get String
FeedAuthor.initials: get String
FeedAuthor.avatar: get ImageProvider?
FeedAuthor.color: get FlAvatarColor
FeedPost: final class
FeedPost.new: const new({required String id, required FeedAuthor author, required DateTime at, String text, List<ImageProvider> images, int likes, bool liked, int comments})
FeedPost.id: get String
FeedPost.author: get FeedAuthor
FeedPost.at: get DateTime
FeedPost.text: get String
FeedPost.images: get List<ImageProvider>
FeedPost.likes: get int
FeedPost.liked: get bool
FeedPost.comments: get int
FeedPost.withLiked: FeedPost(bool liked)
FeedPage: final class
FeedPage.new: const new(List<FeedPost> posts, {String? next})
FeedPage.posts: get List<FeedPost>
FeedPage.next: get String?
FeedNoNetwork: final class implements Exception
FeedNoNetwork.new: const new()
FeedNoNetwork.toString: String()
FeedService: abstract interface class
FeedService.new: new()
FeedService.load: abstract Future<FeedPage>({String? after})
FeedService.arrivals: abstract get Stream<FeedPost>
FeedService.setLiked: abstract Future<void>(String postId, bool liked)
FeedService.hide: abstract Future<void>(String postId)
FeedService.report: abstract Future<void>(String postId)
sampleFeedNow: get DateTime
sampleFeedPosts: get List<FeedPost>
sampleFeedArrival: get FeedPost
SampleFeedService: final class implements FeedService
SampleFeedService.new: new({List<FeedPost>? posts, int pageSize, Duration latency})
SampleFeedService.pageSize: get int
SampleFeedService.latency: get Duration
SampleFeedService.publish: void(FeedPost post)
SampleFeedService.load: Future<FeedPage>({String? after})
SampleFeedService.arrivals: get Stream<FeedPost>
SampleFeedService.setLiked: Future<void>(String postId, bool liked)
SampleFeedService.hide: Future<void>(String postId)
SampleFeedService.report: Future<void>(String postId)
feedL10n: const Map<String, Map<String, String>>
FeedStrings: final class
FeedStrings.new: const new(String language)
FeedStrings.of: factory new(BuildContext context)
FeedStrings.language: get String
FeedStrings.title: get String
FeedStrings.compose: get String
FeedStrings.loading: get String
FeedStrings.refresh: get String
FeedStrings.newPosts: String(int count)
FeedStrings.like: String(String name)
FeedStrings.liked: String(bool liked)
FeedStrings.comment: String(String name)
FeedStrings.share: String(String name)
FeedStrings.more: String(String name)
FeedStrings.author: String(String name)
FeedStrings.hide: get String
FeedStrings.report: get String
FeedStrings.hidden: get String
FeedStrings.reported: get String
FeedStrings.undo: get String
FeedStrings.likes: String(int count)
FeedStrings.comments: String(int count)
FeedStrings.photos: String(int count)
FeedStrings.format: get FlCountFormat
FeedStrings.count: FlPostCount(int count, String Function(int count) read)
FeedStrings.age: String(FlPostAge age)
FeedStrings.likeFailed: get String
FeedStrings.hideFailed: get String
FeedStrings.reportFailed: get String
FeedStrings.refreshFailed: get String
FeedStrings.refreshOffline: get String
FeedStrings.loadingMore: get String
FeedStrings.moreFailed: get String
FeedStrings.moreOffline: get String
FeedStrings.end: get String
FeedStrings.emptyTitle: get String
FeedStrings.emptyMessage: get String
FeedStrings.errorTitle: get String
FeedStrings.errorMessage: get String
FeedStrings.offlineTitle: get String
FeedStrings.offlineMessage: get String
FeedStrings.retry: get String

Strings

Every label in English, French and Arabic. The screen follows the closest Localizations; the same table is in the manifest, ready for your ARB files.

Keyenfrar
feedTitleFeedFilآخر المنشورات
feedLoadingLoading the feedChargement du filجارٍ تحميل المنشورات
feedRefreshRefreshActualiserتحديث
feedNewPostsOne{count} new post{count} nouvelle publicationمنشور جديد واحد
feedNewPostsMany{count} new posts{count} nouvelles publicationsمنشورات جديدة: {count}
feedLikeLike, post by {name}J’aime, publication de {name}إعجاب، منشور {name}
feedLikedLikedJ’aime ajoutéتم الإعجاب
feedUnlikedLike removedJ’aime retiréأُلغي الإعجاب
feedCommentComment, post by {name}Commenter, publication de {name}تعليق، منشور {name}
feedShareShare, post by {name}Partager, publication de {name}مشاركة، منشور {name}
feedMoreMore actions, post by {name}Plus d’actions, publication de {name}مزيد من الإجراءات، منشور {name}
feedAuthorOpen the profile of {name}Voir le profil de {name}عرض الملف الشخصي لـ {name}
feedHideHideMasquerإخفاء
feedReportReportSignalerإبلاغ
feedHiddenPost hidden.Publication masquée.تم إخفاء المنشور.
feedReportedPost reported. Thank you.Publication signalée. Merci.تم الإبلاغ عن المنشور. شكرًا لك.
feedUndoUndoAnnulerتراجع
feedLikesOne{count} like{count} J’aimeإعجاب واحد
feedLikesMany{count} likes{count} J’aimeالإعجابات: {count}
feedCommentsOne{count} comment{count} commentaireتعليق واحد
feedCommentsMany{count} comments{count} commentairesالتعليقات: {count}
feedPhotosOne{count} photo{count} photoصورة واحدة
feedPhotosMany{count} photos{count} photosالصور: {count}
feedNowNowÀ l’instantالآن
feedMinutes{count} min{count} min{count} د
feedHours{count} h{count} h{count} س
feedDays{count} d{count} j{count} ي
feedDate{month}/{day}/{year}{day}/{month}/{year}{day}/{month}/{year}
feedDecimal.,٫
feedGroup, ٬
feedThousandsK k ألف
feedMillionsM M مليون
feedBillionsB Md مليار
feedDigits01234567890123456789٠١٢٣٤٥٦٧٨٩
feedLikeFailedYour like wasn’t saved.Votre J’aime n’a pas été enregistré.لم يُحفظ إعجابك.
feedHideFailedCouldn’t hide the post. It’s back.Impossible de masquer la publication. Elle est revenue.تعذّر إخفاء المنشور. لقد عاد.
feedReportFailedThe report wasn’t sent. Try again.Le signalement n’est pas parti. Réessayez.لم يُرسل البلاغ. أعد المحاولة.
feedRefreshFailedCouldn’t refresh the feed.Impossible d’actualiser le fil.تعذّر تحديث المنشورات.
feedRefreshOfflineYou are offline. The feed may be out of date.Vous êtes hors ligne. Le fil n’est peut-être pas à jour.أنت غير متصل. قد لا تكون المنشورات محدّثة.
feedLoadingMoreLoading more postsChargement de la suiteجارٍ تحميل المزيد
feedMoreFailedMore posts couldn’t load.La suite n’a pas pu se charger.تعذّر تحميل المزيد.
feedMoreOfflineYou are offline. More posts can’t load.Vous êtes hors ligne. La suite ne peut pas se charger.أنت غير متصل. لا يمكن تحميل المزيد.
feedEndYou’re all caught up.Vous avez tout vu.لقد شاهدت كل شيء.
feedEmptyTitleNothing to read yetRien à lire pour l’instantلا شيء للقراءة بعد
feedEmptyMessageFollow people to see their posts here.Suivez des personnes pour voir leurs publications ici.تابع أشخاصًا لترى منشوراتهم هنا.
feedErrorTitleThe feed could not loadImpossible de charger le filتعذّر تحميل المنشورات
feedErrorMessageSomething went wrong on our side.Un problème est survenu de notre côté.حدثت مشكلة من جهتنا.
feedOfflineTitleYou are offlineVous êtes hors ligneأنت غير متصل
feedOfflineMessageCheck your connection, then try again.Vérifiez votre connexion, puis réessayez.تحقق من اتصالك ثم أعد المحاولة.
feedRetryTry againRéessayerأعد المحاولة
feedComposeWrite a postÉcrire une publicationاكتب منشورًا

Reference renders

The 16 images published with version 0.2.5, rendered by the shared harness on macOS with Flutter 3.47.7. Each shows every state side by side, at the size of an iPhone (390 × 844) or of an Android phone (412 × 915). fliin verify feed compares your copy to them.

iOS · Light
Social feed, iOS, Light, text 1.0×, Left to right
Text 1.0× · Left to right
Social feed, iOS, Light, text 1.0×, Right to left
Text 1.0× · Right to left
Social feed, iOS, Light, text 2.0×, Left to right
Text 2.0× · Left to right
Social feed, iOS, Light, text 2.0×, Right to left
Text 2.0× · Right to left
iOS · Dark
Social feed, iOS, Dark, text 1.0×, Left to right
Text 1.0× · Left to right
Social feed, iOS, Dark, text 1.0×, Right to left
Text 1.0× · Right to left
Social feed, iOS, Dark, text 2.0×, Left to right
Text 2.0× · Left to right
Social feed, iOS, Dark, text 2.0×, Right to left
Text 2.0× · Right to left
Android · Light
Social feed, Android, Light, text 1.0×, Left to right
Text 1.0× · Left to right
Social feed, Android, Light, text 1.0×, Right to left
Text 1.0× · Right to left
Social feed, Android, Light, text 2.0×, Left to right
Text 2.0× · Left to right
Social feed, Android, Light, text 2.0×, Right to left
Text 2.0× · Right to left
Android · Dark
Social feed, Android, Dark, text 1.0×, Left to right
Text 1.0× · Left to right
Social feed, Android, Dark, text 1.0×, Right to left
Text 1.0× · Right to left
Social feed, Android, Dark, text 2.0×, Left to right
Text 2.0× · Left to right
Social feed, Android, Dark, text 2.0×, Right to left
Text 2.0× · Right to left

Use it to

  • show the posts of followed people with their images
  • toggle a like on a post at once, saved by the server afterwards
  • offer new posts on demand without moving what is being read
  • hide or report a post with a short delay to cancel
  • keep loading older posts while scrolling down the timeline
Not for
  • a list of products with their prices: use the filtered search (search)
  • the messages of a discussion: use the chat thread (chat)
States
  • Loaded
  • Incoming
  • Hidden
  • Loading
  • Empty
  • Error
  • Offline

Arrows to move, Enter to open, Escape to close.