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.
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 feedAlso 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.
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.
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.
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.
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.
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.
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.
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.
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 StringStrings
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.
| Key | en | fr | ar |
|---|---|---|---|
feedTitle | Feed | Fil | آخر المنشورات |
feedLoading | Loading the feed | Chargement du fil | جارٍ تحميل المنشورات |
feedRefresh | Refresh | Actualiser | تحديث |
feedNewPostsOne | {count} new post | {count} nouvelle publication | منشور جديد واحد |
feedNewPostsMany | {count} new posts | {count} nouvelles publications | منشورات جديدة: {count} |
feedLike | Like, post by {name} | J’aime, publication de {name} | إعجاب، منشور {name} |
feedLiked | Liked | J’aime ajouté | تم الإعجاب |
feedUnliked | Like removed | J’aime retiré | أُلغي الإعجاب |
feedComment | Comment, post by {name} | Commenter, publication de {name} | تعليق، منشور {name} |
feedShare | Share, post by {name} | Partager, publication de {name} | مشاركة، منشور {name} |
feedMore | More actions, post by {name} | Plus d’actions, publication de {name} | مزيد من الإجراءات، منشور {name} |
feedAuthor | Open the profile of {name} | Voir le profil de {name} | عرض الملف الشخصي لـ {name} |
feedHide | Hide | Masquer | إخفاء |
feedReport | Report | Signaler | إبلاغ |
feedHidden | Post hidden. | Publication masquée. | تم إخفاء المنشور. |
feedReported | Post reported. Thank you. | Publication signalée. Merci. | تم الإبلاغ عن المنشور. شكرًا لك. |
feedUndo | Undo | Annuler | تراجع |
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} |
feedNow | Now | À 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 | , | ٬ | |
feedThousands | K | k | ألف |
feedMillions | M | M | مليون |
feedBillions | B | Md | مليار |
feedDigits | 0123456789 | 0123456789 | ٠١٢٣٤٥٦٧٨٩ |
feedLikeFailed | Your like wasn’t saved. | Votre J’aime n’a pas été enregistré. | لم يُحفظ إعجابك. |
feedHideFailed | Couldn’t hide the post. It’s back. | Impossible de masquer la publication. Elle est revenue. | تعذّر إخفاء المنشور. لقد عاد. |
feedReportFailed | The report wasn’t sent. Try again. | Le signalement n’est pas parti. Réessayez. | لم يُرسل البلاغ. أعد المحاولة. |
feedRefreshFailed | Couldn’t refresh the feed. | Impossible d’actualiser le fil. | تعذّر تحديث المنشورات. |
feedRefreshOffline | You are offline. The feed may be out of date. | Vous êtes hors ligne. Le fil n’est peut-être pas à jour. | أنت غير متصل. قد لا تكون المنشورات محدّثة. |
feedLoadingMore | Loading more posts | Chargement de la suite | جارٍ تحميل المزيد |
feedMoreFailed | More posts couldn’t load. | La suite n’a pas pu se charger. | تعذّر تحميل المزيد. |
feedMoreOffline | You are offline. More posts can’t load. | Vous êtes hors ligne. La suite ne peut pas se charger. | أنت غير متصل. لا يمكن تحميل المزيد. |
feedEnd | You’re all caught up. | Vous avez tout vu. | لقد شاهدت كل شيء. |
feedEmptyTitle | Nothing to read yet | Rien à lire pour l’instant | لا شيء للقراءة بعد |
feedEmptyMessage | Follow people to see their posts here. | Suivez des personnes pour voir leurs publications ici. | تابع أشخاصًا لترى منشوراتهم هنا. |
feedErrorTitle | The feed could not load | Impossible de charger le fil | تعذّر تحميل المنشورات |
feedErrorMessage | Something went wrong on our side. | Un problème est survenu de notre côté. | حدثت مشكلة من جهتنا. |
feedOfflineTitle | You are offline | Vous êtes hors ligne | أنت غير متصل |
feedOfflineMessage | Check your connection, then try again. | Vérifiez votre connexion, puis réessayez. | تحقق من اتصالك ثم أعد المحاولة. |
feedRetry | Try again | Réessayer | أعد المحاولة |
feedCompose | Write 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.
















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