🎉 Initial Commit
This commit is contained in:
56
lib/main.dart
Normal file
56
lib/main.dart
Normal file
@ -0,0 +1,56 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:easy_localization_loader/easy_localization_loader.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:responsive_framework/responsive_framework.dart';
|
||||
import 'package:surface/providers/sn_network.dart';
|
||||
import 'package:surface/providers/theme.dart';
|
||||
import 'package:surface/providers/userinfo.dart';
|
||||
import 'package:surface/router.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await EasyLocalization.ensureInitialized();
|
||||
|
||||
runApp(const SolianApp());
|
||||
}
|
||||
|
||||
class SolianApp extends StatelessWidget {
|
||||
const SolianApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBreakpoints.builder(
|
||||
child: EasyLocalization(
|
||||
path: 'assets/translations',
|
||||
supportedLocales: [Locale('en', 'US'), Locale('zh', 'CN')],
|
||||
fallbackLocale: Locale('en', 'US'),
|
||||
useFallbackTranslations: true,
|
||||
assetLoader: JsonAssetLoader(),
|
||||
child: MultiProvider(
|
||||
providers: [
|
||||
Provider(create: (_) => SnNetworkProvider()),
|
||||
ChangeNotifierProvider(create: (_) => UserProvider()),
|
||||
ChangeNotifierProvider(create: (_) => ThemeProvider()),
|
||||
],
|
||||
child: Builder(builder: (context) {
|
||||
final th = context.watch<ThemeProvider>();
|
||||
return MaterialApp.router(
|
||||
theme: th.theme.light,
|
||||
darkTheme: th.theme.dark,
|
||||
locale: context.locale,
|
||||
supportedLocales: context.supportedLocales,
|
||||
localizationsDelegates: context.localizationDelegates,
|
||||
routerConfig: appRouter,
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
breakpoints: [
|
||||
const Breakpoint(start: 0, end: 450, name: MOBILE),
|
||||
const Breakpoint(start: 451, end: 800, name: TABLET),
|
||||
const Breakpoint(start: 801, end: 1920, name: DESKTOP),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
35
lib/providers/sn_network.dart
Normal file
35
lib/providers/sn_network.dart
Normal file
@ -0,0 +1,35 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:dio_smart_retry/dio_smart_retry.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:native_dio_adapter/native_dio_adapter.dart';
|
||||
|
||||
const kUseLocalNetwork = false;
|
||||
|
||||
class SnNetworkProvider {
|
||||
late final Dio client;
|
||||
|
||||
SnNetworkProvider() {
|
||||
client = Dio();
|
||||
|
||||
client.options.baseUrl = kUseLocalNetwork
|
||||
? 'http://localhost:8001'
|
||||
: 'https://api.sn.solsynth.dev';
|
||||
|
||||
client.interceptors.add(RetryInterceptor(
|
||||
dio: client,
|
||||
retries: 3,
|
||||
retryDelays: const [
|
||||
Duration(milliseconds: 300),
|
||||
Duration(milliseconds: 1000),
|
||||
Duration(milliseconds: 3000),
|
||||
],
|
||||
));
|
||||
|
||||
if (!kIsWeb && Platform.isAndroid || Platform.isIOS || Platform.isMacOS) {
|
||||
// Switch to native implementation if possible
|
||||
client.httpClientAdapter = NativeAdapter();
|
||||
}
|
||||
}
|
||||
}
|
10
lib/providers/theme.dart
Normal file
10
lib/providers/theme.dart
Normal file
@ -0,0 +1,10 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:surface/theme.dart';
|
||||
|
||||
class ThemeProvider extends ChangeNotifier {
|
||||
late ThemeSet theme;
|
||||
|
||||
ThemeProvider() {
|
||||
theme = createAppThemeSet();
|
||||
}
|
||||
}
|
3
lib/providers/userinfo.dart
Normal file
3
lib/providers/userinfo.dart
Normal file
@ -0,0 +1,3 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class UserProvider extends ChangeNotifier {}
|
33
lib/router.dart
Normal file
33
lib/router.dart
Normal file
@ -0,0 +1,33 @@
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:surface/screens/account.dart';
|
||||
import 'package:surface/screens/explore.dart';
|
||||
import 'package:surface/screens/home.dart';
|
||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||
|
||||
final appRouter = GoRouter(
|
||||
routes: [
|
||||
ShellRoute(
|
||||
builder: (context, state, child) => AppScaffold(
|
||||
body: child,
|
||||
showBottomNavigation: true,
|
||||
),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/',
|
||||
name: 'home',
|
||||
builder: (context, state) => const HomeScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/posts',
|
||||
name: 'explore',
|
||||
builder: (context, state) => const ExploreScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/account',
|
||||
name: 'account',
|
||||
builder: (context, state) => const AccountScreen(),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
21
lib/screens/account.dart
Normal file
21
lib/screens/account.dart
Normal file
@ -0,0 +1,21 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||
|
||||
class AccountScreen extends StatefulWidget {
|
||||
const AccountScreen({super.key});
|
||||
|
||||
@override
|
||||
State<AccountScreen> createState() => _AccountScreenState();
|
||||
}
|
||||
|
||||
class _AccountScreenState extends State<AccountScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppScaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("screenHome").tr(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
61
lib/screens/explore.dart
Normal file
61
lib/screens/explore.dart
Normal file
@ -0,0 +1,61 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:surface/providers/sn_network.dart';
|
||||
import 'package:surface/types/post.dart';
|
||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||
import 'package:very_good_infinite_list/very_good_infinite_list.dart';
|
||||
|
||||
class ExploreScreen extends StatefulWidget {
|
||||
const ExploreScreen({super.key});
|
||||
|
||||
@override
|
||||
State<ExploreScreen> createState() => _ExploreScreenState();
|
||||
}
|
||||
|
||||
class _ExploreScreenState extends State<ExploreScreen> {
|
||||
bool _isBusy = true;
|
||||
|
||||
final List<SnPost> _posts = List.empty(growable: true);
|
||||
int _postCount = 0;
|
||||
|
||||
void _fetchPosts() async {
|
||||
setState(() => _isBusy = true);
|
||||
|
||||
final sn = context.read<SnNetworkProvider>();
|
||||
|
||||
final resp = await sn.client.get('/cgi/co/posts', queryParameters: {
|
||||
'take': 10,
|
||||
'offset': 0,
|
||||
});
|
||||
|
||||
_postCount = resp.data['count'];
|
||||
_posts.addAll(
|
||||
resp.data['data']?.map((e) => SnPost.fromJson(e)).cast<SnPost>() ?? []);
|
||||
|
||||
if (mounted) setState(() => _isBusy = false);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_fetchPosts();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppScaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('screenExplore').tr(),
|
||||
),
|
||||
body: InfiniteList(
|
||||
itemCount: _posts.length,
|
||||
isLoading: _isBusy,
|
||||
onFetchData: _fetchPosts,
|
||||
itemBuilder: (context, idx) {
|
||||
return Text(_posts[idx].toString());
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
21
lib/screens/home.dart
Normal file
21
lib/screens/home.dart
Normal file
@ -0,0 +1,21 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:surface/widgets/navigation/app_scaffold.dart';
|
||||
|
||||
class HomeScreen extends StatefulWidget {
|
||||
const HomeScreen({super.key});
|
||||
|
||||
@override
|
||||
State<HomeScreen> createState() => _HomeScreenState();
|
||||
}
|
||||
|
||||
class _HomeScreenState extends State<HomeScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppScaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("screenHome").tr(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
25
lib/theme.dart
Normal file
25
lib/theme.dart
Normal file
@ -0,0 +1,25 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ThemeSet {
|
||||
ThemeData light;
|
||||
ThemeData dark;
|
||||
|
||||
ThemeSet({required this.light, required this.dark});
|
||||
}
|
||||
|
||||
ThemeSet createAppThemeSet() {
|
||||
return ThemeSet(
|
||||
light: createAppTheme(),
|
||||
dark: createAppTheme(),
|
||||
);
|
||||
}
|
||||
|
||||
ThemeData createAppTheme() {
|
||||
return ThemeData(
|
||||
useMaterial3: false,
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: Colors.indigo,
|
||||
brightness: Brightness.light,
|
||||
),
|
||||
);
|
||||
}
|
92
lib/types/post.dart
Normal file
92
lib/types/post.dart
Normal file
@ -0,0 +1,92 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'post.freezed.dart';
|
||||
part 'post.g.dart';
|
||||
|
||||
@freezed
|
||||
class SnPost with _$SnPost {
|
||||
const factory SnPost({
|
||||
required int id,
|
||||
required DateTime createdAt,
|
||||
required DateTime updatedAt,
|
||||
required DateTime? deletedAt,
|
||||
required String type,
|
||||
required dynamic body,
|
||||
required String language,
|
||||
required String? alias,
|
||||
required String aliasPrefix,
|
||||
required List<dynamic> tags,
|
||||
required List<dynamic> categories,
|
||||
required dynamic reactions,
|
||||
required dynamic replies,
|
||||
required dynamic replyId,
|
||||
required dynamic repostId,
|
||||
required dynamic replyTo,
|
||||
required dynamic repostTo,
|
||||
required dynamic visibleUsersList,
|
||||
required dynamic invisibleUsersList,
|
||||
required int visibility,
|
||||
required DateTime? editedAt,
|
||||
required DateTime? pinnedAt,
|
||||
required DateTime? lockedAt,
|
||||
required bool isDraft,
|
||||
required DateTime publishedAt,
|
||||
required dynamic publishedUntil,
|
||||
required int totalUpvote,
|
||||
required int totalDownvote,
|
||||
required int? realmId,
|
||||
required dynamic realm,
|
||||
required int publisherId,
|
||||
required SnPublisher publisher,
|
||||
required SnMetric metric,
|
||||
}) = _SnPost;
|
||||
|
||||
factory SnPost.fromJson(Map<String, Object?> json) => _$SnPostFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SnBody with _$SnBody {
|
||||
const factory SnBody({
|
||||
required List<String> attachments,
|
||||
required String content,
|
||||
required dynamic location,
|
||||
required dynamic thumbnail,
|
||||
required dynamic title,
|
||||
}) = _SnBody;
|
||||
|
||||
factory SnBody.fromJson(Map<String, Object?> json) => _$SnBodyFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SnMetric with _$SnMetric {
|
||||
const factory SnMetric({
|
||||
required int replyCount,
|
||||
required int reactionCount,
|
||||
}) = _SnMetric;
|
||||
|
||||
factory SnMetric.fromJson(Map<String, Object?> json) =>
|
||||
_$SnMetricFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SnPublisher with _$SnPublisher {
|
||||
const factory SnPublisher({
|
||||
required int id,
|
||||
required DateTime createdAt,
|
||||
required DateTime updatedAt,
|
||||
required DateTime? deletedAt,
|
||||
required int type,
|
||||
required String name,
|
||||
required String nick,
|
||||
required String description,
|
||||
required String avatar,
|
||||
required String banner,
|
||||
required int totalUpvote,
|
||||
required int totalDownvote,
|
||||
required int? realmId,
|
||||
required int accountId,
|
||||
}) = _SnPublisher;
|
||||
|
||||
factory SnPublisher.fromJson(Map<String, Object?> json) =>
|
||||
_$SnPublisherFromJson(json);
|
||||
}
|
1745
lib/types/post.freezed.dart
Normal file
1745
lib/types/post.freezed.dart
Normal file
File diff suppressed because it is too large
Load Diff
158
lib/types/post.g.dart
Normal file
158
lib/types/post.g.dart
Normal file
@ -0,0 +1,158 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'post.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
_$SnPostImpl _$$SnPostImplFromJson(Map<String, dynamic> json) => _$SnPostImpl(
|
||||
id: (json['id'] as num).toInt(),
|
||||
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||
updatedAt: DateTime.parse(json['updatedAt'] as String),
|
||||
deletedAt: json['deletedAt'] == null
|
||||
? null
|
||||
: DateTime.parse(json['deletedAt'] as String),
|
||||
type: json['type'] as String,
|
||||
body: json['body'],
|
||||
language: json['language'] as String,
|
||||
alias: json['alias'] as String?,
|
||||
aliasPrefix: json['aliasPrefix'] as String,
|
||||
tags: json['tags'] as List<dynamic>,
|
||||
categories: json['categories'] as List<dynamic>,
|
||||
reactions: json['reactions'],
|
||||
replies: json['replies'],
|
||||
replyId: json['replyId'],
|
||||
repostId: json['repostId'],
|
||||
replyTo: json['replyTo'],
|
||||
repostTo: json['repostTo'],
|
||||
visibleUsersList: json['visibleUsersList'],
|
||||
invisibleUsersList: json['invisibleUsersList'],
|
||||
visibility: (json['visibility'] as num).toInt(),
|
||||
editedAt: json['editedAt'] == null
|
||||
? null
|
||||
: DateTime.parse(json['editedAt'] as String),
|
||||
pinnedAt: json['pinnedAt'] == null
|
||||
? null
|
||||
: DateTime.parse(json['pinnedAt'] as String),
|
||||
lockedAt: json['lockedAt'] == null
|
||||
? null
|
||||
: DateTime.parse(json['lockedAt'] as String),
|
||||
isDraft: json['isDraft'] as bool,
|
||||
publishedAt: DateTime.parse(json['publishedAt'] as String),
|
||||
publishedUntil: json['publishedUntil'],
|
||||
totalUpvote: (json['totalUpvote'] as num).toInt(),
|
||||
totalDownvote: (json['totalDownvote'] as num).toInt(),
|
||||
realmId: (json['realmId'] as num?)?.toInt(),
|
||||
realm: json['realm'],
|
||||
publisherId: (json['publisherId'] as num).toInt(),
|
||||
publisher:
|
||||
SnPublisher.fromJson(json['publisher'] as Map<String, dynamic>),
|
||||
metric: SnMetric.fromJson(json['metric'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SnPostImplToJson(_$SnPostImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'createdAt': instance.createdAt.toIso8601String(),
|
||||
'updatedAt': instance.updatedAt.toIso8601String(),
|
||||
'deletedAt': instance.deletedAt?.toIso8601String(),
|
||||
'type': instance.type,
|
||||
'body': instance.body,
|
||||
'language': instance.language,
|
||||
'alias': instance.alias,
|
||||
'aliasPrefix': instance.aliasPrefix,
|
||||
'tags': instance.tags,
|
||||
'categories': instance.categories,
|
||||
'reactions': instance.reactions,
|
||||
'replies': instance.replies,
|
||||
'replyId': instance.replyId,
|
||||
'repostId': instance.repostId,
|
||||
'replyTo': instance.replyTo,
|
||||
'repostTo': instance.repostTo,
|
||||
'visibleUsersList': instance.visibleUsersList,
|
||||
'invisibleUsersList': instance.invisibleUsersList,
|
||||
'visibility': instance.visibility,
|
||||
'editedAt': instance.editedAt?.toIso8601String(),
|
||||
'pinnedAt': instance.pinnedAt?.toIso8601String(),
|
||||
'lockedAt': instance.lockedAt?.toIso8601String(),
|
||||
'isDraft': instance.isDraft,
|
||||
'publishedAt': instance.publishedAt.toIso8601String(),
|
||||
'publishedUntil': instance.publishedUntil,
|
||||
'totalUpvote': instance.totalUpvote,
|
||||
'totalDownvote': instance.totalDownvote,
|
||||
'realmId': instance.realmId,
|
||||
'realm': instance.realm,
|
||||
'publisherId': instance.publisherId,
|
||||
'publisher': instance.publisher,
|
||||
'metric': instance.metric,
|
||||
};
|
||||
|
||||
_$SnBodyImpl _$$SnBodyImplFromJson(Map<String, dynamic> json) => _$SnBodyImpl(
|
||||
attachments: (json['attachments'] as List<dynamic>)
|
||||
.map((e) => e as String)
|
||||
.toList(),
|
||||
content: json['content'] as String,
|
||||
location: json['location'],
|
||||
thumbnail: json['thumbnail'],
|
||||
title: json['title'],
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SnBodyImplToJson(_$SnBodyImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'attachments': instance.attachments,
|
||||
'content': instance.content,
|
||||
'location': instance.location,
|
||||
'thumbnail': instance.thumbnail,
|
||||
'title': instance.title,
|
||||
};
|
||||
|
||||
_$SnMetricImpl _$$SnMetricImplFromJson(Map<String, dynamic> json) =>
|
||||
_$SnMetricImpl(
|
||||
replyCount: (json['replyCount'] as num).toInt(),
|
||||
reactionCount: (json['reactionCount'] as num).toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SnMetricImplToJson(_$SnMetricImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'replyCount': instance.replyCount,
|
||||
'reactionCount': instance.reactionCount,
|
||||
};
|
||||
|
||||
_$SnPublisherImpl _$$SnPublisherImplFromJson(Map<String, dynamic> json) =>
|
||||
_$SnPublisherImpl(
|
||||
id: (json['id'] as num).toInt(),
|
||||
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||
updatedAt: DateTime.parse(json['updatedAt'] as String),
|
||||
deletedAt: json['deletedAt'] == null
|
||||
? null
|
||||
: DateTime.parse(json['deletedAt'] as String),
|
||||
type: (json['type'] as num).toInt(),
|
||||
name: json['name'] as String,
|
||||
nick: json['nick'] as String,
|
||||
description: json['description'] as String,
|
||||
avatar: json['avatar'] as String,
|
||||
banner: json['banner'] as String,
|
||||
totalUpvote: (json['totalUpvote'] as num).toInt(),
|
||||
totalDownvote: (json['totalDownvote'] as num).toInt(),
|
||||
realmId: (json['realmId'] as num?)?.toInt(),
|
||||
accountId: (json['accountId'] as num).toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$$SnPublisherImplToJson(_$SnPublisherImpl instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'createdAt': instance.createdAt.toIso8601String(),
|
||||
'updatedAt': instance.updatedAt.toIso8601String(),
|
||||
'deletedAt': instance.deletedAt?.toIso8601String(),
|
||||
'type': instance.type,
|
||||
'name': instance.name,
|
||||
'nick': instance.nick,
|
||||
'description': instance.description,
|
||||
'avatar': instance.avatar,
|
||||
'banner': instance.banner,
|
||||
'totalUpvote': instance.totalUpvote,
|
||||
'totalDownvote': instance.totalDownvote,
|
||||
'realmId': instance.realmId,
|
||||
'accountId': instance.accountId,
|
||||
};
|
11
lib/widgets/navigation/app_background.dart
Normal file
11
lib/widgets/navigation/app_background.dart
Normal file
@ -0,0 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AppBackground extends StatelessWidget {
|
||||
final Widget child;
|
||||
const AppBackground({super.key, required this.child});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ScaffoldMessenger(child: child);
|
||||
}
|
||||
}
|
33
lib/widgets/navigation/app_bottom_navigation.dart
Normal file
33
lib/widgets/navigation/app_bottom_navigation.dart
Normal file
@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:surface/widgets/navigation/app_destinations.dart';
|
||||
|
||||
class AppBottomNavigationBar extends StatefulWidget {
|
||||
const AppBottomNavigationBar({super.key});
|
||||
|
||||
@override
|
||||
State<AppBottomNavigationBar> createState() => _AppBottomNavigationBarState();
|
||||
}
|
||||
|
||||
class _AppBottomNavigationBarState extends State<AppBottomNavigationBar> {
|
||||
int _currentIndex = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BottomNavigationBar(
|
||||
currentIndex: _currentIndex,
|
||||
type: BottomNavigationBarType.fixed,
|
||||
showUnselectedLabels: false,
|
||||
items: appDestinations.map((ele) {
|
||||
return BottomNavigationBarItem(
|
||||
icon: ele.icon,
|
||||
label: ele.label,
|
||||
);
|
||||
}).toList(),
|
||||
onTap: (idx) {
|
||||
setState(() => _currentIndex = idx);
|
||||
GoRouter.of(context).goNamed(appDestinations[idx].screen);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
33
lib/widgets/navigation/app_destinations.dart
Normal file
33
lib/widgets/navigation/app_destinations.dart
Normal file
@ -0,0 +1,33 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
|
||||
class AppNavDestination {
|
||||
final String label;
|
||||
final String screen;
|
||||
final Widget icon;
|
||||
|
||||
AppNavDestination({
|
||||
required this.label,
|
||||
required this.screen,
|
||||
required this.icon,
|
||||
});
|
||||
}
|
||||
|
||||
List<AppNavDestination> appDestinations = [
|
||||
AppNavDestination(
|
||||
icon: Icon(Symbols.home),
|
||||
screen: 'home',
|
||||
label: tr('screenHome'),
|
||||
),
|
||||
AppNavDestination(
|
||||
icon: Icon(Symbols.explore),
|
||||
screen: 'explore',
|
||||
label: tr('screenExplore'),
|
||||
),
|
||||
AppNavDestination(
|
||||
icon: Icon(Symbols.account_circle),
|
||||
screen: 'account',
|
||||
label: tr('screenAccount'),
|
||||
),
|
||||
];
|
28
lib/widgets/navigation/app_scaffold.dart
Normal file
28
lib/widgets/navigation/app_scaffold.dart
Normal file
@ -0,0 +1,28 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:responsive_framework/responsive_framework.dart';
|
||||
import 'package:surface/widgets/navigation/app_background.dart';
|
||||
import 'package:surface/widgets/navigation/app_bottom_navigation.dart';
|
||||
|
||||
class AppScaffold extends StatelessWidget {
|
||||
final PreferredSizeWidget? appBar;
|
||||
final Widget? body;
|
||||
final bool? showBottomNavigation;
|
||||
const AppScaffold(
|
||||
{super.key, this.appBar, this.body, this.showBottomNavigation});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isShowBottomNavigation = (showBottomNavigation ?? false)
|
||||
? ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE)
|
||||
: false;
|
||||
|
||||
return AppBackground(
|
||||
child: Scaffold(
|
||||
appBar: appBar,
|
||||
body: body,
|
||||
bottomNavigationBar:
|
||||
isShowBottomNavigation ? AppBottomNavigationBar() : null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
0
lib/widgets/post/post_list.dart
Normal file
0
lib/widgets/post/post_list.dart
Normal file
Reference in New Issue
Block a user