refactor(apps): 主题系统迁移至 ColorScheme + 扩展架构并支持 Dark Mode
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import 'package:social_app/core/network/i_api_client.dart';
|
||||
|
||||
import 'models/schedule_item_model.dart';
|
||||
import 'package:social_app/data/repositories/models/schedule_item_model.dart';
|
||||
|
||||
class CalendarApi {
|
||||
final IApiClient _client;
|
||||
|
||||
@@ -1,306 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum ScheduleSourceType { manual, imported, agentGenerated }
|
||||
|
||||
enum ScheduleStatus { active, archived }
|
||||
|
||||
class ScheduleItemModel {
|
||||
final String id;
|
||||
final String ownerId;
|
||||
final int permission;
|
||||
final bool isOwner;
|
||||
final String title;
|
||||
final String? description;
|
||||
final DateTime startAt;
|
||||
final DateTime? endAt;
|
||||
final String timezone;
|
||||
final ScheduleMetadata? metadata;
|
||||
final ScheduleSourceType sourceType;
|
||||
final ScheduleStatus status;
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
|
||||
static const int permissionView = 1;
|
||||
static const int permissionInvite = 2;
|
||||
static const int permissionEdit = 4;
|
||||
|
||||
bool get canEdit => isOwner || (permission & permissionEdit) != 0;
|
||||
bool get canInvite => isOwner || (permission & permissionInvite) != 0;
|
||||
bool get canDelete => isOwner;
|
||||
|
||||
ScheduleItemModel({
|
||||
required this.id,
|
||||
required this.ownerId,
|
||||
this.permission = 1,
|
||||
this.isOwner = false,
|
||||
required this.title,
|
||||
this.description,
|
||||
required this.startAt,
|
||||
this.endAt,
|
||||
this.timezone = 'Asia/Shanghai',
|
||||
this.metadata,
|
||||
this.sourceType = ScheduleSourceType.manual,
|
||||
this.status = ScheduleStatus.active,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
}) : createdAt = createdAt ?? DateTime.now(),
|
||||
updatedAt = updatedAt ?? DateTime.now();
|
||||
|
||||
ScheduleItemModel copyWith({
|
||||
String? id,
|
||||
String? ownerId,
|
||||
int? permission,
|
||||
bool? isOwner,
|
||||
String? title,
|
||||
String? description,
|
||||
DateTime? startAt,
|
||||
DateTime? endAt,
|
||||
String? timezone,
|
||||
ScheduleMetadata? metadata,
|
||||
ScheduleSourceType? sourceType,
|
||||
ScheduleStatus? status,
|
||||
DateTime? createdAt,
|
||||
DateTime? updatedAt,
|
||||
}) {
|
||||
return ScheduleItemModel(
|
||||
id: id ?? this.id,
|
||||
ownerId: ownerId ?? this.ownerId,
|
||||
permission: permission ?? this.permission,
|
||||
isOwner: isOwner ?? this.isOwner,
|
||||
title: title ?? this.title,
|
||||
description: description ?? this.description,
|
||||
startAt: startAt ?? this.startAt,
|
||||
endAt: endAt ?? this.endAt,
|
||||
timezone: timezone ?? this.timezone,
|
||||
metadata: metadata ?? this.metadata,
|
||||
sourceType: sourceType ?? this.sourceType,
|
||||
status: status ?? this.status,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
updatedAt: updatedAt ?? this.updatedAt,
|
||||
);
|
||||
}
|
||||
|
||||
factory ScheduleItemModel.fromJson(Map<String, dynamic> json) {
|
||||
return ScheduleItemModel(
|
||||
id: json['id'] as String,
|
||||
ownerId: json['owner_id'] as String? ?? '',
|
||||
permission: json['permission'] as int? ?? 1,
|
||||
isOwner: json['is_owner'] as bool? ?? false,
|
||||
title: json['title'] as String,
|
||||
description: json['description'] as String?,
|
||||
startAt: DateTime.parse(json['start_at'] as String).toLocal(),
|
||||
endAt: json['end_at'] != null
|
||||
? DateTime.parse(json['end_at'] as String).toLocal()
|
||||
: null,
|
||||
timezone: (json['timezone'] as String?) ?? 'UTC',
|
||||
metadata: json['metadata'] is Map<String, dynamic>
|
||||
? ScheduleMetadata.fromJson(json['metadata'] as Map<String, dynamic>)
|
||||
: null,
|
||||
sourceType: _sourceTypeFromApi(json['source_type'] as String?),
|
||||
status: _statusFromApi(json['status'] as String?),
|
||||
createdAt: json['created_at'] != null
|
||||
? DateTime.parse(json['created_at'] as String).toLocal()
|
||||
: DateTime.now(),
|
||||
updatedAt: json['updated_at'] != null
|
||||
? DateTime.parse(json['updated_at'] as String).toLocal()
|
||||
: DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toCreateJson() {
|
||||
return {
|
||||
'title': title,
|
||||
'description': description,
|
||||
'start_at': startAt.toUtc().toIso8601String(),
|
||||
'end_at': endAt?.toUtc().toIso8601String(),
|
||||
'timezone': timezone,
|
||||
'metadata': metadata?.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, dynamic> toUpdateJson() {
|
||||
return {
|
||||
'title': title,
|
||||
'description': description,
|
||||
'start_at': startAt.toUtc().toIso8601String(),
|
||||
'end_at': endAt?.toUtc().toIso8601String(),
|
||||
'timezone': timezone,
|
||||
'metadata': metadata?.toJson(),
|
||||
'status': _statusToApi(status),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ScheduleMetadata {
|
||||
final String? color;
|
||||
final String? location;
|
||||
final String? notes;
|
||||
final int? reminderMinutes;
|
||||
final List<Attachment> attachments;
|
||||
final int version;
|
||||
final Map<String, dynamic> raw;
|
||||
|
||||
ScheduleMetadata({
|
||||
this.color,
|
||||
this.location,
|
||||
this.notes,
|
||||
this.reminderMinutes,
|
||||
List<Attachment>? attachments,
|
||||
this.version = 1,
|
||||
Map<String, dynamic>? raw,
|
||||
}) : attachments = attachments ?? const [],
|
||||
raw = raw ?? const {};
|
||||
|
||||
ScheduleMetadata copyWith({
|
||||
String? color,
|
||||
String? location,
|
||||
String? notes,
|
||||
int? reminderMinutes,
|
||||
List<Attachment>? attachments,
|
||||
int? version,
|
||||
Map<String, dynamic>? raw,
|
||||
}) {
|
||||
return ScheduleMetadata(
|
||||
color: color ?? this.color,
|
||||
location: location ?? this.location,
|
||||
notes: notes ?? this.notes,
|
||||
reminderMinutes: reminderMinutes ?? this.reminderMinutes,
|
||||
attachments: attachments ?? this.attachments,
|
||||
version: version ?? this.version,
|
||||
raw: raw ?? this.raw,
|
||||
);
|
||||
}
|
||||
|
||||
factory ScheduleMetadata.fromJson(Map<String, dynamic> json) {
|
||||
final rawAttachments = json['attachments'];
|
||||
final attachments = rawAttachments is List
|
||||
? rawAttachments
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.map(Attachment.fromJson)
|
||||
.toList()
|
||||
: <Attachment>[];
|
||||
return ScheduleMetadata(
|
||||
color: json['color'] as String?,
|
||||
location: json['location'] as String?,
|
||||
notes: json['notes'] as String?,
|
||||
reminderMinutes: json['reminder_minutes'] as int?,
|
||||
attachments: attachments,
|
||||
version: (json['version'] as int?) ?? 1,
|
||||
raw: Map<String, dynamic>.from(json),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'color': color,
|
||||
'location': location,
|
||||
'notes': notes,
|
||||
'reminder_minutes': reminderMinutes,
|
||||
'attachments': attachments.map((item) => item.toJson()).toList(),
|
||||
'version': version,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Attachment {
|
||||
final String name;
|
||||
final List<String> visibleTo;
|
||||
final String? url;
|
||||
final String? note;
|
||||
final String? content;
|
||||
final String type;
|
||||
|
||||
Attachment({
|
||||
required this.name,
|
||||
this.visibleTo = const [],
|
||||
this.url,
|
||||
this.note,
|
||||
this.content,
|
||||
this.type = 'document',
|
||||
});
|
||||
|
||||
Attachment copyWith({
|
||||
String? name,
|
||||
List<String>? visibleTo,
|
||||
String? url,
|
||||
String? note,
|
||||
String? content,
|
||||
String? type,
|
||||
}) {
|
||||
return Attachment(
|
||||
name: name ?? this.name,
|
||||
visibleTo: visibleTo ?? this.visibleTo,
|
||||
url: url ?? this.url,
|
||||
note: note ?? this.note,
|
||||
content: content ?? this.content,
|
||||
type: type ?? this.type,
|
||||
);
|
||||
}
|
||||
|
||||
factory Attachment.fromJson(Map<String, dynamic> json) {
|
||||
final rawVisibleTo = json['visible_to'];
|
||||
final visibleTo = rawVisibleTo is List
|
||||
? rawVisibleTo.map((item) => item.toString()).toList()
|
||||
: <String>[];
|
||||
return Attachment(
|
||||
name: (json['name'] as String?) ?? '',
|
||||
visibleTo: visibleTo,
|
||||
url: json['url'] as String?,
|
||||
note: json['note'] as String?,
|
||||
content: json['content'] as String?,
|
||||
type: (json['type'] as String?) ?? 'document',
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'name': name,
|
||||
'visible_to': visibleTo,
|
||||
'url': url,
|
||||
'note': note,
|
||||
'content': content,
|
||||
'type': type,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
ScheduleSourceType _sourceTypeFromApi(String? raw) {
|
||||
switch (raw) {
|
||||
case 'imported':
|
||||
return ScheduleSourceType.imported;
|
||||
case 'agent_generated':
|
||||
return ScheduleSourceType.agentGenerated;
|
||||
case 'manual':
|
||||
default:
|
||||
return ScheduleSourceType.manual;
|
||||
}
|
||||
}
|
||||
|
||||
ScheduleStatus _statusFromApi(String? raw) {
|
||||
switch (raw) {
|
||||
case 'completed':
|
||||
case 'canceled':
|
||||
case 'archived':
|
||||
return ScheduleStatus.archived;
|
||||
case 'active':
|
||||
default:
|
||||
return ScheduleStatus.active;
|
||||
}
|
||||
}
|
||||
|
||||
String _statusToApi(ScheduleStatus status) {
|
||||
switch (status) {
|
||||
case ScheduleStatus.active:
|
||||
return 'active';
|
||||
case ScheduleStatus.archived:
|
||||
return 'archived';
|
||||
}
|
||||
}
|
||||
|
||||
const defaultColors = [
|
||||
Color(0xFF3B82F6),
|
||||
Color(0xFF8B5CF6),
|
||||
Color(0xFF10B981),
|
||||
Color(0xFFF59E0B),
|
||||
Color(0xFFEF4444),
|
||||
];
|
||||
@@ -1,144 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import '../../../../core/cache/cache_entry.dart';
|
||||
import '../../../../core/cache/cache_policy.dart';
|
||||
import '../../../../core/cache/hybrid_cache_store.dart';
|
||||
import '../models/schedule_item_model.dart';
|
||||
|
||||
class CalendarRepository {
|
||||
final HybridCacheStore store;
|
||||
final CachePolicy policy;
|
||||
final DateTime Function() now;
|
||||
final Future<List<ScheduleItemModel>> Function(DateTime date)
|
||||
loadDayFromRemote;
|
||||
final Future<List<ScheduleItemModel>> Function(DateTime start, DateTime end)
|
||||
loadMonthFromRemote;
|
||||
|
||||
final Map<String, Future<void>> _refreshInFlight = <String, Future<void>>{};
|
||||
|
||||
CalendarRepository({
|
||||
required this.store,
|
||||
required this.loadDayFromRemote,
|
||||
required this.loadMonthFromRemote,
|
||||
CachePolicy? policy,
|
||||
DateTime Function()? now,
|
||||
}) : policy =
|
||||
policy ??
|
||||
const CachePolicy(
|
||||
softTtl: Duration(minutes: 2),
|
||||
hardTtl: Duration(minutes: 30),
|
||||
minRefreshInterval: Duration(minutes: 1),
|
||||
),
|
||||
now = now ?? DateTime.now;
|
||||
|
||||
static String dayKey(DateTime date) {
|
||||
final day =
|
||||
'${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
|
||||
return 'calendar:day:$day';
|
||||
}
|
||||
|
||||
static String monthKey(DateTime date) {
|
||||
return 'calendar:month:${date.year}-${date.month.toString().padLeft(2, '0')}';
|
||||
}
|
||||
|
||||
Future<List<ScheduleItemModel>> getDayEvents(
|
||||
DateTime date, {
|
||||
bool forceRefresh = false,
|
||||
}) async {
|
||||
final key = dayKey(date);
|
||||
if (forceRefresh) {
|
||||
return _refreshDayAndRead(date, key);
|
||||
}
|
||||
|
||||
final cached = await store.read<CacheEntry<List<ScheduleItemModel>>>(key);
|
||||
if (cached == null) {
|
||||
return _refreshDayAndRead(date, key);
|
||||
}
|
||||
|
||||
final decision = policy.evaluate(now: now(), fetchedAt: cached.fetchedAt);
|
||||
if (decision.shouldRefreshInBackground) {
|
||||
_refreshDayInBackground(date, key);
|
||||
}
|
||||
if (decision.mustBlockForNetwork || !decision.canUseCached) {
|
||||
return _refreshDayAndRead(date, key);
|
||||
}
|
||||
return cached.value;
|
||||
}
|
||||
|
||||
Future<List<ScheduleItemModel>> getMonthEvents(
|
||||
DateTime monthStart, {
|
||||
bool forceRefresh = false,
|
||||
}) async {
|
||||
final key = monthKey(monthStart);
|
||||
if (forceRefresh) {
|
||||
return _refreshMonthAndRead(monthStart, key);
|
||||
}
|
||||
final cached = await store.read<CacheEntry<List<ScheduleItemModel>>>(key);
|
||||
if (cached == null) {
|
||||
return _refreshMonthAndRead(monthStart, key);
|
||||
}
|
||||
final decision = policy.evaluate(now: now(), fetchedAt: cached.fetchedAt);
|
||||
if (decision.shouldRefreshInBackground) {
|
||||
_refreshMonthInBackground(monthStart, key);
|
||||
}
|
||||
if (decision.mustBlockForNetwork || !decision.canUseCached) {
|
||||
return _refreshMonthAndRead(monthStart, key);
|
||||
}
|
||||
return cached.value;
|
||||
}
|
||||
|
||||
Future<List<ScheduleItemModel>> _refreshDayAndRead(
|
||||
DateTime date,
|
||||
String key,
|
||||
) async {
|
||||
await _refreshDay(date, key);
|
||||
final cached = await store.read<CacheEntry<List<ScheduleItemModel>>>(key);
|
||||
return cached?.value ?? const <ScheduleItemModel>[];
|
||||
}
|
||||
|
||||
Future<List<ScheduleItemModel>> _refreshMonthAndRead(
|
||||
DateTime monthStart,
|
||||
String key,
|
||||
) async {
|
||||
await _refreshMonth(monthStart, key);
|
||||
final cached = await store.read<CacheEntry<List<ScheduleItemModel>>>(key);
|
||||
return cached?.value ?? const <ScheduleItemModel>[];
|
||||
}
|
||||
|
||||
Future<void> _refreshDay(DateTime date, String key) async {
|
||||
final remote = await loadDayFromRemote(date);
|
||||
await store.write<CacheEntry<List<ScheduleItemModel>>>(
|
||||
key,
|
||||
CacheEntry<List<ScheduleItemModel>>(value: remote, fetchedAt: now()),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _refreshMonth(DateTime monthStart, String key) async {
|
||||
final start = DateTime(monthStart.year, monthStart.month, 1);
|
||||
final end = DateTime(monthStart.year, monthStart.month + 1, 0, 23, 59, 59);
|
||||
final remote = await loadMonthFromRemote(start, end);
|
||||
await store.write<CacheEntry<List<ScheduleItemModel>>>(
|
||||
key,
|
||||
CacheEntry<List<ScheduleItemModel>>(value: remote, fetchedAt: now()),
|
||||
);
|
||||
}
|
||||
|
||||
void _refreshDayInBackground(DateTime date, String key) {
|
||||
_refreshInBackground(key, () => _refreshDay(date, key));
|
||||
}
|
||||
|
||||
void _refreshMonthInBackground(DateTime monthStart, String key) {
|
||||
_refreshInBackground(key, () => _refreshMonth(monthStart, key));
|
||||
}
|
||||
|
||||
void _refreshInBackground(String key, Future<void> Function() taskFactory) {
|
||||
if (_refreshInFlight.containsKey(key)) {
|
||||
return;
|
||||
}
|
||||
final task = taskFactory().whenComplete(() {
|
||||
_refreshInFlight.remove(key);
|
||||
});
|
||||
_refreshInFlight[key] = task;
|
||||
unawaited(task);
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
import 'package:social_app/core/network/i_api_client.dart';
|
||||
import 'package:social_app/core/cache/cache_invalidator.dart';
|
||||
import 'package:social_app/app/di/injection.dart';
|
||||
|
||||
import '../calendar_api.dart';
|
||||
import '../models/schedule_item_model.dart';
|
||||
|
||||
class CalendarService {
|
||||
final IApiClient _apiClient;
|
||||
CalendarApi? _calendarApi;
|
||||
|
||||
CalendarService({required IApiClient apiClient}) : _apiClient = apiClient;
|
||||
|
||||
CalendarApi get _api {
|
||||
final api = _calendarApi;
|
||||
if (api != null) {
|
||||
return api;
|
||||
}
|
||||
final created = CalendarApi(_apiClient);
|
||||
_calendarApi = created;
|
||||
return created;
|
||||
}
|
||||
|
||||
Future<List<ScheduleItemModel>> getEventsForDay(DateTime date) async {
|
||||
final start = DateTime(date.year, date.month, date.day);
|
||||
final end = DateTime(date.year, date.month, date.day, 23, 59, 59);
|
||||
return getEventsForRange(start, end);
|
||||
}
|
||||
|
||||
Future<List<ScheduleItemModel>> getEventsForRange(
|
||||
DateTime start,
|
||||
DateTime end,
|
||||
) async {
|
||||
return _api.listByRange(startAt: start, endAt: end);
|
||||
}
|
||||
|
||||
Future<ScheduleItemModel?> getEventById(String id) async {
|
||||
return _api.getById(id);
|
||||
}
|
||||
|
||||
Future<ScheduleItemModel> addEvent(ScheduleItemModel event) async {
|
||||
final created = await _api.create(event);
|
||||
_invalidateEventCache(created);
|
||||
return created;
|
||||
}
|
||||
|
||||
Future<ScheduleItemModel> updateEvent(ScheduleItemModel event) async {
|
||||
final updated = await _api.update(event);
|
||||
_invalidateEventCache(updated);
|
||||
return updated;
|
||||
}
|
||||
|
||||
Future<ScheduleItemModel?> archiveEvent(String id) async {
|
||||
final event = await getEventById(id);
|
||||
if (event == null) {
|
||||
return null;
|
||||
}
|
||||
final updatedEvent = await updateEvent(
|
||||
event.copyWith(status: ScheduleStatus.archived),
|
||||
);
|
||||
_invalidateEventCache(updatedEvent);
|
||||
return updatedEvent;
|
||||
}
|
||||
|
||||
void _invalidateEventCache(ScheduleItemModel event) {
|
||||
try {
|
||||
final invalidator = sl<CacheInvalidator>();
|
||||
var current = DateTime(
|
||||
event.startAt.year,
|
||||
event.startAt.month,
|
||||
event.startAt.day,
|
||||
);
|
||||
final end = DateTime(
|
||||
event.endAt?.year ?? event.startAt.year,
|
||||
event.endAt?.month ?? event.startAt.month,
|
||||
event.endAt?.day ?? event.startAt.day,
|
||||
);
|
||||
while (!current.isAfter(end)) {
|
||||
invalidator.invalidateCalendarDay(current);
|
||||
current = current.add(const Duration(days: 1));
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
Future<void> deleteEvent(String id) async {
|
||||
final event = await getEventById(id);
|
||||
if (event != null) {
|
||||
_invalidateEventCache(event);
|
||||
}
|
||||
await _api.delete(id);
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum CalendarViewType { day, month }
|
||||
|
||||
class CalendarState {
|
||||
final CalendarViewType viewType;
|
||||
final DateTime selectedDate;
|
||||
|
||||
CalendarState({required this.viewType, required this.selectedDate});
|
||||
|
||||
CalendarState copyWith({CalendarViewType? viewType, DateTime? selectedDate}) {
|
||||
return CalendarState(
|
||||
viewType: viewType ?? this.viewType,
|
||||
selectedDate: selectedDate ?? this.selectedDate,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CalendarStateManager extends ChangeNotifier {
|
||||
CalendarState _state;
|
||||
|
||||
CalendarStateManager()
|
||||
: _state = CalendarState(
|
||||
viewType: CalendarViewType.month,
|
||||
selectedDate: DateTime.now(),
|
||||
);
|
||||
|
||||
CalendarState get state => _state;
|
||||
|
||||
CalendarViewType get viewType => _state.viewType;
|
||||
DateTime get selectedDate => _state.selectedDate;
|
||||
|
||||
void setViewType(CalendarViewType type) {
|
||||
_state = _state.copyWith(viewType: type);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setSelectedDate(DateTime date) {
|
||||
_state = _state.copyWith(selectedDate: date);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void resetToToday() {
|
||||
final now = DateTime.now();
|
||||
_state = CalendarState(
|
||||
viewType: CalendarViewType.month,
|
||||
selectedDate: DateTime(now.year, now.month, now.day),
|
||||
);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import '../../data/models/schedule_item_model.dart';
|
||||
import '../../../../data/repositories/models/schedule_item_model.dart';
|
||||
import 'day_timeline_metrics.dart';
|
||||
import 'day_view_scale.dart';
|
||||
|
||||
|
||||
@@ -2,21 +2,21 @@ import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
import '../../../../app/router/app_routes.dart';
|
||||
import '../../../home/presentation/navigation/home_return_policy.dart';
|
||||
import '../../../../app/router/home_return_policy.dart';
|
||||
|
||||
import '../../../../app/di/injection.dart';
|
||||
import '../../../../core/l10n/l10n.dart';
|
||||
import '../../../../core/theme/design_tokens.dart';
|
||||
import '../../../../data/repositories/calendar_repository.dart';
|
||||
import '../../../../shared/widgets/app_pressable.dart';
|
||||
import '../../data/models/schedule_item_model.dart';
|
||||
import '../../data/services/calendar_repository.dart';
|
||||
import '../calendar_state_manager.dart';
|
||||
import '../../../../shared/widgets/bottom_dock.dart';
|
||||
import '../../../../shared/state/calendar_state_manager.dart';
|
||||
import '../../../../data/repositories/models/schedule_item_model.dart';
|
||||
import '../calendar_time_utils.dart';
|
||||
import '../utils/event_color_resolver.dart';
|
||||
import '../dayweek/day_event_layout_engine.dart';
|
||||
import '../dayweek/day_timeline_metrics.dart';
|
||||
import '../dayweek/day_view_scale.dart';
|
||||
import '../widgets/bottom_dock.dart';
|
||||
|
||||
class CalendarDayWeekScreen extends StatefulWidget {
|
||||
final DateTime? initialDate;
|
||||
@@ -52,6 +52,8 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
|
||||
late List<DateTime> _monthDates;
|
||||
List<ScheduleItemModel> _events = const [];
|
||||
|
||||
ColorScheme get _colorScheme => Theme.of(context).colorScheme;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -107,7 +109,7 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.todoBg,
|
||||
backgroundColor: _colorScheme.surface,
|
||||
body: PopScope(
|
||||
canPop: false,
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
@@ -244,18 +246,18 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
|
||||
height: 36,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.messageBtnWrap,
|
||||
color: _colorScheme.surfaceContainerLow,
|
||||
borderRadius: BorderRadius.circular(AppRadius.xl),
|
||||
border: Border.all(color: AppColors.messageBtnBorder),
|
||||
border: Border.all(color: _colorScheme.outlineVariant),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icon(
|
||||
LucideIcons.chevronLeft,
|
||||
size: 16,
|
||||
color: AppColors.slate700,
|
||||
color: _colorScheme.onSurface,
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
AnimatedSwitcher(
|
||||
@@ -277,10 +279,10 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
|
||||
child: Text(
|
||||
monthLabel,
|
||||
key: ValueKey(monthLabel),
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate700,
|
||||
color: _colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -297,17 +299,17 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
|
||||
height: 36,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.messageBtnWrap,
|
||||
color: _colorScheme.surfaceContainerLow,
|
||||
borderRadius: BorderRadius.circular(AppRadius.xl),
|
||||
border: Border.all(color: AppColors.messageBtnBorder),
|
||||
border: Border.all(color: _colorScheme.outlineVariant),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
context.l10n.calendarToday,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate700,
|
||||
color: _colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -328,13 +330,13 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
|
||||
width: 36,
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.blue600,
|
||||
color: _colorScheme.primary,
|
||||
borderRadius: BorderRadius.circular(AppRadius.full),
|
||||
),
|
||||
child: const Icon(
|
||||
child: Icon(
|
||||
LucideIcons.plus,
|
||||
size: 20,
|
||||
color: AppColors.white,
|
||||
color: _colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -454,7 +456,9 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
|
||||
_weekdayLabel(date),
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: isWeekend ? AppColors.slate400 : AppColors.slate600,
|
||||
color: isWeekend
|
||||
? _colorScheme.onSurfaceVariant
|
||||
: _colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
@@ -464,7 +468,9 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? AppColors.blue100 : Colors.transparent,
|
||||
color: isSelected
|
||||
? _colorScheme.primaryContainer
|
||||
: _colorScheme.surface.withValues(alpha: 0),
|
||||
borderRadius: BorderRadius.circular(AppRadius.full),
|
||||
),
|
||||
child: Center(
|
||||
@@ -474,8 +480,10 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
|
||||
fontSize: 17,
|
||||
fontWeight: isSelected ? FontWeight.w700 : FontWeight.w600,
|
||||
color: isSelected
|
||||
? AppColors.blue600
|
||||
: (isWeekend ? AppColors.slate400 : AppColors.slate900),
|
||||
? _colorScheme.primary
|
||||
: (isWeekend
|
||||
? _colorScheme.onSurfaceVariant
|
||||
: _colorScheme.onSurface),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -572,7 +580,7 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
|
||||
top: adjustedY,
|
||||
left: eventAreaLeft,
|
||||
right: 0,
|
||||
child: Container(height: 1, color: AppColors.border),
|
||||
child: Container(height: 1, color: _colorScheme.outlineVariant),
|
||||
),
|
||||
Positioned(
|
||||
top: labelTop,
|
||||
@@ -584,7 +592,7 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate400,
|
||||
color: _colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -613,16 +621,16 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
|
||||
width: DayTimelineMetrics.timeLabelWidth,
|
||||
height: 18,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.red500,
|
||||
color: _colorScheme.error,
|
||||
borderRadius: BorderRadius.circular(9),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
formatHm(now),
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.white,
|
||||
color: _colorScheme.onError,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -632,7 +640,7 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
|
||||
child: Container(
|
||||
height: 2,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.red500,
|
||||
color: _colorScheme.error,
|
||||
borderRadius: BorderRadius.circular(99),
|
||||
),
|
||||
),
|
||||
@@ -651,7 +659,7 @@ class _CalendarDayWeekScreenState extends State<CalendarDayWeekScreen>
|
||||
final isArchived = layout.event.status == ScheduleStatus.archived;
|
||||
Color eventColor;
|
||||
if (isArchived) {
|
||||
eventColor = AppColors.slate400;
|
||||
eventColor = _colorScheme.onSurfaceVariant;
|
||||
} else {
|
||||
eventColor = resolveEventColor(
|
||||
status: layout.event.status,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../core/theme/design_tokens.dart';
|
||||
import '../widgets/create_event_sheet.dart';
|
||||
|
||||
class CalendarEventCreateScreen extends StatelessWidget {
|
||||
@@ -10,8 +9,9 @@ class CalendarEventCreateScreen extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.background,
|
||||
backgroundColor: colorScheme.surface,
|
||||
body: SafeArea(
|
||||
child: CreateEventSheet(initialDate: initialDate, pageMode: true),
|
||||
),
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'package:go_router/go_router.dart';
|
||||
import 'package:social_app/core/l10n/l10n.dart';
|
||||
import '../../../../app/di/injection.dart';
|
||||
import '../../../../app/router/app_routes.dart';
|
||||
import '../../../../features/notification/data/services/local_notification_service.dart';
|
||||
import '../../../../data/services/local_notification_service.dart';
|
||||
import '../../../../core/theme/design_tokens.dart';
|
||||
import '../../../../shared/widgets/app_loading_indicator.dart';
|
||||
import '../../../../shared/widgets/back_title_page_header.dart';
|
||||
@@ -12,8 +12,8 @@ import '../../../../shared/widgets/detail_header_action_menu.dart';
|
||||
import '../../../../shared/widgets/destructive_action_sheet.dart';
|
||||
import '../../../../shared/widgets/toast/toast.dart';
|
||||
import '../../../../shared/widgets/toast/toast_type.dart';
|
||||
import '../../data/services/calendar_service.dart';
|
||||
import '../../data/models/schedule_item_model.dart';
|
||||
import '../../../../data/services/calendar_service.dart';
|
||||
import '../../../../data/repositories/models/schedule_item_model.dart';
|
||||
import '../utils/event_color_resolver.dart';
|
||||
|
||||
enum _CalendarHeaderAction { edit, delete, share, archive }
|
||||
@@ -32,6 +32,8 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
ScheduleItemModel? _event;
|
||||
bool _loading = true;
|
||||
|
||||
ColorScheme get _colorScheme => Theme.of(context).colorScheme;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -60,14 +62,17 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
}
|
||||
if (_event == null) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.background,
|
||||
backgroundColor: _colorScheme.surface,
|
||||
body: SafeArea(
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [AppColors.homeBackgroundTop, AppColors.background],
|
||||
colors: [
|
||||
_colorScheme.surfaceContainerLow,
|
||||
_colorScheme.surface,
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
@@ -89,7 +94,7 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate700,
|
||||
color: _colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.sm),
|
||||
@@ -98,7 +103,7 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: AppColors.slate500,
|
||||
color: _colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -115,14 +120,14 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
|
||||
final event = _event!;
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.background,
|
||||
backgroundColor: _colorScheme.surface,
|
||||
body: SafeArea(
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [AppColors.homeBackgroundTop, AppColors.background],
|
||||
colors: [_colorScheme.surfaceContainerLow, _colorScheme.surface],
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
@@ -255,12 +260,12 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(AppSpacing.lg),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.white,
|
||||
color: _colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.xl),
|
||||
border: Border.all(color: AppColors.borderTertiary),
|
||||
border: Border.all(color: _colorScheme.outlineVariant),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColors.slate200.withValues(alpha: 0.38),
|
||||
color: _colorScheme.shadow.withValues(alpha: 0.18),
|
||||
blurRadius: AppRadius.lg,
|
||||
offset: const Offset(0, AppSpacing.xs),
|
||||
),
|
||||
@@ -289,10 +294,10 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
event.title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.slate900,
|
||||
color: _colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.xs),
|
||||
@@ -307,9 +312,9 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.surfaceInfoLight,
|
||||
color: _colorScheme.primaryContainer,
|
||||
borderRadius: BorderRadius.circular(AppRadius.lg),
|
||||
border: Border.all(color: AppColors.borderQuaternary),
|
||||
border: Border.all(color: _colorScheme.outlineVariant),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -319,7 +324,7 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate500,
|
||||
color: _colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.xs),
|
||||
@@ -329,7 +334,7 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.slate800,
|
||||
color: _colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -356,9 +361,9 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(AppSpacing.lg),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.white,
|
||||
color: _colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(AppRadius.xl),
|
||||
border: Border.all(color: AppColors.borderSecondary),
|
||||
border: Border.all(color: _colorScheme.outlineVariant),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -368,7 +373,7 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate500,
|
||||
color: _colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: AppSpacing.md),
|
||||
@@ -386,10 +391,10 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
width: AppSpacing.xxl * 3,
|
||||
child: Text(
|
||||
context.l10n.calendarDetailColor,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate500,
|
||||
color: _colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -399,7 +404,7 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: AppColors.borderSecondary),
|
||||
border: Border.all(color: _colorScheme.outlineVariant),
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -419,9 +424,9 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(AppSpacing.lg),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.surfaceSecondary,
|
||||
color: _colorScheme.surfaceContainerLow,
|
||||
borderRadius: BorderRadius.circular(AppRadius.xl),
|
||||
border: Border.all(color: AppColors.borderSecondary),
|
||||
border: Border.all(color: _colorScheme.outlineVariant),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -431,7 +436,7 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate500,
|
||||
color: _colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
if (event.metadata?.location?.trim().isNotEmpty ?? false) ...[
|
||||
@@ -570,13 +575,11 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: isArchived
|
||||
? AppColors.feedbackWarningSurface
|
||||
: AppColors.feedbackSuccessSurface,
|
||||
? _colorScheme.errorContainer
|
||||
: _colorScheme.tertiaryContainer,
|
||||
borderRadius: BorderRadius.circular(AppRadius.full),
|
||||
border: Border.all(
|
||||
color: isArchived
|
||||
? AppColors.feedbackWarningBorder
|
||||
: AppColors.feedbackSuccessBorder,
|
||||
color: isArchived ? _colorScheme.error : _colorScheme.tertiary,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
@@ -587,8 +590,8 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: isArchived
|
||||
? AppColors.feedbackWarningText
|
||||
: AppColors.feedbackSuccessText,
|
||||
? _colorScheme.onErrorContainer
|
||||
: _colorScheme.onTertiaryContainer,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -604,10 +607,10 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
width: AppSpacing.xxl * 3,
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate500,
|
||||
color: _colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -619,7 +622,7 @@ class _CalendarEventDetailScreenState extends State<CalendarEventDetailScreen> {
|
||||
fontSize: 14,
|
||||
height: multiline ? 1.4 : 1.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate800,
|
||||
color: _colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -2,11 +2,10 @@ import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../app/di/injection.dart';
|
||||
import '../../../../core/l10n/l10n.dart';
|
||||
import '../../../../core/theme/design_tokens.dart';
|
||||
import '../../../../shared/widgets/app_loading_indicator.dart';
|
||||
import '../widgets/create_event_sheet.dart';
|
||||
import '../../data/models/schedule_item_model.dart';
|
||||
import '../../data/services/calendar_service.dart';
|
||||
import '../../../../data/repositories/models/schedule_item_model.dart';
|
||||
import '../../../../data/services/calendar_service.dart';
|
||||
|
||||
class CalendarEventEditScreen extends StatefulWidget {
|
||||
final String eventId;
|
||||
@@ -61,7 +60,7 @@ class _CalendarEventEditScreenState extends State<CalendarEventEditScreen> {
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.background,
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
body: SafeArea(
|
||||
child: CreateEventSheet(editingEvent: _event, pageMode: true),
|
||||
),
|
||||
|
||||
@@ -2,11 +2,10 @@ import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../app/di/injection.dart';
|
||||
import '../../../../core/l10n/l10n.dart';
|
||||
import '../../../../core/theme/design_tokens.dart';
|
||||
import '../../../../shared/widgets/app_loading_indicator.dart';
|
||||
import '../../../../shared/widgets/back_title_page_header.dart';
|
||||
import '../../data/models/schedule_item_model.dart';
|
||||
import '../../data/services/calendar_service.dart';
|
||||
import '../../../../data/repositories/models/schedule_item_model.dart';
|
||||
import '../../../../data/services/calendar_service.dart';
|
||||
import '../widgets/calendar_share_dialog.dart';
|
||||
|
||||
class CalendarEventShareScreen extends StatefulWidget {
|
||||
@@ -63,7 +62,7 @@ class _CalendarEventShareScreenState extends State<CalendarEventShareScreen> {
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.background,
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
|
||||
@@ -6,14 +6,14 @@ import 'package:social_app/core/l10n/l10n.dart';
|
||||
import '../../../../app/di/injection.dart';
|
||||
import '../../../../app/router/app_routes.dart';
|
||||
import '../../../../core/theme/design_tokens.dart';
|
||||
import '../../../../data/repositories/calendar_repository.dart';
|
||||
import '../../../../shared/widgets/app_pressable.dart';
|
||||
import '../../../home/presentation/navigation/home_return_policy.dart';
|
||||
import '../calendar_state_manager.dart';
|
||||
import '../../../../shared/widgets/bottom_dock.dart';
|
||||
import '../../../../shared/state/calendar_state_manager.dart';
|
||||
import '../../../../app/router/home_return_policy.dart';
|
||||
import '../calendar_time_utils.dart';
|
||||
import '../utils/event_color_resolver.dart';
|
||||
import '../widgets/bottom_dock.dart';
|
||||
import '../../data/models/schedule_item_model.dart';
|
||||
import '../../data/services/calendar_repository.dart';
|
||||
import '../../../../data/repositories/models/schedule_item_model.dart';
|
||||
|
||||
class CalendarMonthScreen extends StatefulWidget {
|
||||
final bool resetToToday;
|
||||
@@ -31,6 +31,8 @@ class _CalendarMonthScreenState extends State<CalendarMonthScreen>
|
||||
late DateTime _selectedDate;
|
||||
final Map<String, List<ScheduleItemModel>> _eventsByDay = {};
|
||||
|
||||
ColorScheme get _colorScheme => Theme.of(context).colorScheme;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -79,7 +81,7 @@ class _CalendarMonthScreenState extends State<CalendarMonthScreen>
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: AppColors.todoBg,
|
||||
backgroundColor: _colorScheme.surface,
|
||||
body: PopScope(
|
||||
canPop: false,
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
@@ -136,18 +138,18 @@ class _CalendarMonthScreenState extends State<CalendarMonthScreen>
|
||||
child: Text(
|
||||
l10n.calendarMonthHeader(_currentMonth.month),
|
||||
key: ValueKey(_currentMonth.month),
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.slate900,
|
||||
color: _colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
const Icon(
|
||||
Icon(
|
||||
LucideIcons.chevronDown,
|
||||
size: 16,
|
||||
color: AppColors.slate900,
|
||||
color: _colorScheme.onSurface,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -161,9 +163,11 @@ class _CalendarMonthScreenState extends State<CalendarMonthScreen>
|
||||
height: 36,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.messageBtnWrap,
|
||||
color: _colorScheme.surfaceContainerLow,
|
||||
borderRadius: BorderRadius.circular(AppRadius.xl),
|
||||
border: Border.all(color: AppColors.messageBtnBorder),
|
||||
border: Border.all(
|
||||
color: _colorScheme.outlineVariant,
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
@@ -171,7 +175,7 @@ class _CalendarMonthScreenState extends State<CalendarMonthScreen>
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate700,
|
||||
color: _colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -192,13 +196,13 @@ class _CalendarMonthScreenState extends State<CalendarMonthScreen>
|
||||
width: 36,
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.blue600,
|
||||
color: _colorScheme.primary,
|
||||
borderRadius: BorderRadius.circular(AppRadius.full),
|
||||
),
|
||||
child: const Icon(
|
||||
child: Icon(
|
||||
LucideIcons.plus,
|
||||
size: 20,
|
||||
color: AppColors.white,
|
||||
color: _colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -229,7 +233,7 @@ class _CalendarMonthScreenState extends State<CalendarMonthScreen>
|
||||
return Column(
|
||||
children: [
|
||||
_buildWeekdayHeader(),
|
||||
Container(height: 1, color: AppColors.border),
|
||||
Container(height: 1, color: _colorScheme.outlineVariant),
|
||||
..._buildWeeks(),
|
||||
],
|
||||
);
|
||||
@@ -259,10 +263,10 @@ class _CalendarMonthScreenState extends State<CalendarMonthScreen>
|
||||
child: Center(
|
||||
child: Text(
|
||||
day,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate400,
|
||||
color: _colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -294,7 +298,7 @@ class _CalendarMonthScreenState extends State<CalendarMonthScreen>
|
||||
for (var weekStart = 0; weekStart < totalCells; weekStart += 7) {
|
||||
weeks.add(_buildWeekRow(weekStart, startWeekday, daysInMonth));
|
||||
if (weekStart + 7 < totalCells) {
|
||||
weeks.add(Container(height: 1, color: AppColors.border));
|
||||
weeks.add(Container(height: 1, color: _colorScheme.outlineVariant));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,7 +341,9 @@ class _CalendarMonthScreenState extends State<CalendarMonthScreen>
|
||||
width: 36,
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? AppColors.blue100 : Colors.transparent,
|
||||
color: isSelected
|
||||
? _colorScheme.primaryContainer
|
||||
: _colorScheme.surface.withValues(alpha: 0),
|
||||
borderRadius: BorderRadius.circular(AppRadius.full),
|
||||
),
|
||||
child: Center(
|
||||
@@ -349,8 +355,8 @@ class _CalendarMonthScreenState extends State<CalendarMonthScreen>
|
||||
? FontWeight.w600
|
||||
: FontWeight.normal,
|
||||
color: isSelected
|
||||
? AppColors.blue600
|
||||
: AppColors.slate900,
|
||||
? _colorScheme.primary
|
||||
: _colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -447,9 +453,9 @@ class _CalendarMonthScreenState extends State<CalendarMonthScreen>
|
||||
},
|
||||
child: Text(
|
||||
'+$remainingCount',
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 9,
|
||||
color: AppColors.slate500,
|
||||
color: _colorScheme.onSurfaceVariant,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
@@ -469,7 +475,7 @@ class _CalendarMonthScreenState extends State<CalendarMonthScreen>
|
||||
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: AppColors.white,
|
||||
backgroundColor: _colorScheme.surface,
|
||||
builder: (context) {
|
||||
return StatefulBuilder(
|
||||
builder: (context, setSheetState) {
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../core/theme/design_tokens.dart';
|
||||
import '../../data/models/schedule_item_model.dart';
|
||||
import '../../../../data/repositories/models/schedule_item_model.dart';
|
||||
|
||||
Color resolveEventColor({
|
||||
required ScheduleStatus status,
|
||||
required String? colorHex,
|
||||
}) {
|
||||
if (status == ScheduleStatus.archived) {
|
||||
return AppColors.slate400;
|
||||
return const Color(0xFF64748B);
|
||||
}
|
||||
|
||||
if (colorHex == null || colorHex.isEmpty) {
|
||||
return AppColors.blue600;
|
||||
return const Color(0xFF3B82F6);
|
||||
}
|
||||
|
||||
try {
|
||||
return Color(int.parse(colorHex.replaceFirst('#', '0xFF')));
|
||||
} catch (_) {
|
||||
return AppColors.blue600;
|
||||
return const Color(0xFF3B82F6);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
import '../../../../core/theme/design_tokens.dart';
|
||||
|
||||
enum DockTab { todo, calendar }
|
||||
|
||||
class BottomDock extends StatelessWidget {
|
||||
final DockTab activeTab;
|
||||
final VoidCallback? onTodoTap;
|
||||
final VoidCallback? onCalendarTap;
|
||||
final VoidCallback? onHomeTap;
|
||||
|
||||
const BottomDock({
|
||||
super.key,
|
||||
required this.activeTab,
|
||||
this.onTodoTap,
|
||||
this.onCalendarTap,
|
||||
this.onHomeTap,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 72,
|
||||
padding: const EdgeInsets.only(
|
||||
left: AppSpacing.xl,
|
||||
right: AppSpacing.xl,
|
||||
top: AppSpacing.md,
|
||||
bottom: AppSpacing.sm,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [_buildToggle(), _buildHomeBtn()],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildToggle() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.todoToggleBg,
|
||||
borderRadius: BorderRadius.circular(AppRadius.xxl),
|
||||
border: Border.all(color: AppColors.todoToggleBorder),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColors.slate200.withValues(alpha: 0.45),
|
||||
blurRadius: AppRadius.sm,
|
||||
offset: const Offset(0, AppSpacing.xs / 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
_buildToggleItem(
|
||||
icon: LucideIcons.listTodo,
|
||||
isActive: activeTab == DockTab.todo,
|
||||
onTap: onTodoTap,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
_buildToggleItem(
|
||||
icon: LucideIcons.calendar,
|
||||
isActive: activeTab == DockTab.calendar,
|
||||
onTap: onCalendarTap,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildToggleItem({
|
||||
required IconData icon,
|
||||
required bool isActive,
|
||||
VoidCallback? onTap,
|
||||
}) {
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
borderRadius: BorderRadius.circular(AppRadius.xl),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 140),
|
||||
curve: Curves.easeOut,
|
||||
width: 44,
|
||||
height: 44,
|
||||
decoration: BoxDecoration(
|
||||
color: isActive ? AppColors.todoToggleActiveBg : Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(AppRadius.xl),
|
||||
border: Border.all(
|
||||
color: isActive
|
||||
? AppColors.todoToggleActiveBorder
|
||||
: Colors.transparent,
|
||||
),
|
||||
),
|
||||
child: Icon(
|
||||
icon,
|
||||
size: 20,
|
||||
color: isActive ? AppColors.blue600 : AppColors.slate700,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHomeBtn() {
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
key: const ValueKey('bottom_dock_home_button'),
|
||||
onTap: onHomeTap,
|
||||
borderRadius: BorderRadius.circular(AppRadius.xl),
|
||||
child: Container(
|
||||
width: 44,
|
||||
height: 44,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.todoToggleBg,
|
||||
borderRadius: BorderRadius.circular(AppRadius.xl),
|
||||
border: Border.all(color: AppColors.todoToggleBorder),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: AppColors.slate200.withValues(alpha: 0.42),
|
||||
blurRadius: AppRadius.sm,
|
||||
offset: const Offset(0, AppSpacing.xs / 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Icon(
|
||||
LucideIcons.home,
|
||||
size: 20,
|
||||
color: AppColors.slate700,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,9 @@ class CalendarShareDialog extends StatefulWidget {
|
||||
return showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
backgroundColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.surface.withValues(alpha: 0),
|
||||
builder: (context) => CalendarShareDialog(
|
||||
eventId: eventId,
|
||||
eventTitle: eventTitle,
|
||||
@@ -108,13 +110,14 @@ class _CalendarShareDialogState extends State<CalendarShareDialog> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.background,
|
||||
color: colorScheme.surface,
|
||||
borderRadius: const BorderRadius.vertical(
|
||||
top: Radius.circular(AppRadius.lg),
|
||||
),
|
||||
@@ -200,6 +203,7 @@ class _CalendarShareDialogState extends State<CalendarShareDialog> {
|
||||
bool value,
|
||||
ValueChanged<bool>? onChanged,
|
||||
) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final enabled = onChanged != null;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: AppSpacing.xs),
|
||||
@@ -211,13 +215,17 @@ class _CalendarShareDialogState extends State<CalendarShareDialog> {
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(color: enabled ? null : Colors.grey),
|
||||
style: TextStyle(
|
||||
color: enabled
|
||||
? colorScheme.onSurface
|
||||
: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
description,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: enabled ? Colors.grey : Colors.grey.shade400,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
import 'package:social_app/core/l10n/l10n.dart';
|
||||
import '../../../../app/di/injection.dart';
|
||||
import '../../../../features/notification/data/services/local_notification_service.dart';
|
||||
import '../../../../data/services/local_notification_service.dart';
|
||||
import '../../../../core/theme/design_tokens.dart';
|
||||
import '../../../../shared/widgets/app_loading_indicator.dart';
|
||||
import '../../../../shared/widgets/app_selection_sheet.dart';
|
||||
@@ -11,8 +11,10 @@ import '../../../../shared/widgets/back_title_page_header.dart';
|
||||
import '../../../../shared/widgets/toast/toast.dart';
|
||||
import '../../../../shared/widgets/toast/toast_type.dart';
|
||||
import 'date_time_picker_sheet.dart';
|
||||
import '../../data/models/schedule_item_model.dart';
|
||||
import '../../data/services/calendar_service.dart';
|
||||
import '../../../../data/repositories/models/schedule_item_model.dart';
|
||||
import '../../../../data/services/calendar_service.dart';
|
||||
|
||||
final _defaultColors = AppColorPalette.light.eventPresetColors;
|
||||
|
||||
class CreateEventSheet extends StatefulWidget {
|
||||
final DateTime? initialDate;
|
||||
@@ -36,7 +38,9 @@ class CreateEventSheet extends StatefulWidget {
|
||||
return showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
backgroundColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.surface.withValues(alpha: 0),
|
||||
builder: (context) =>
|
||||
CreateEventSheet(initialDate: initialDate, onSaved: onSaved),
|
||||
);
|
||||
@@ -50,7 +54,9 @@ class CreateEventSheet extends StatefulWidget {
|
||||
return showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
backgroundColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.surface.withValues(alpha: 0),
|
||||
builder: (context) =>
|
||||
CreateEventSheet(editingEvent: event, onSaved: onSaved),
|
||||
);
|
||||
@@ -149,12 +155,13 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
if (widget.pageMode) {
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: () => FocusScope.of(context).unfocus(),
|
||||
child: Container(
|
||||
color: AppColors.background,
|
||||
color: colorScheme.surface,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
@@ -175,8 +182,8 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
),
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).size.height * 0.85,
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColors.white,
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
child: Column(
|
||||
@@ -188,7 +195,7 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
width: AppSpacing.xl + AppSpacing.sm,
|
||||
height: AppSpacing.xs,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.slate200,
|
||||
color: colorScheme.outlineVariant,
|
||||
borderRadius: BorderRadius.circular(AppRadius.full),
|
||||
),
|
||||
),
|
||||
@@ -204,6 +211,7 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
}
|
||||
|
||||
Widget _buildPageHeader() {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
return BackTitlePageHeader(
|
||||
title: _isEditing
|
||||
? context.l10n.calendarCreateEditTitle
|
||||
@@ -226,14 +234,16 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
? const AppLoadingIndicator(
|
||||
variant: AppLoadingVariant.button,
|
||||
size: 18,
|
||||
trackColor: AppColors.blue200,
|
||||
trackColor: null,
|
||||
)
|
||||
: Text(
|
||||
context.l10n.commonSave,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: enabled ? AppColors.blue600 : AppColors.slate400,
|
||||
color: enabled
|
||||
? colorScheme.primary
|
||||
: colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -244,6 +254,7 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
return Container(
|
||||
height: 56,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
@@ -261,10 +272,10 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
borderRadius: BorderRadius.circular(AppRadius.full),
|
||||
),
|
||||
),
|
||||
child: const Icon(
|
||||
child: Icon(
|
||||
LucideIcons.x,
|
||||
size: AppSpacing.xxl,
|
||||
color: AppColors.slate700,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -272,10 +283,10 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
_isEditing
|
||||
? context.l10n.calendarCreateEditTitle
|
||||
: context.l10n.calendarCreateNewTitle,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
ValueListenableBuilder<TextEditingValue>(
|
||||
@@ -297,7 +308,7 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
? const AppLoadingIndicator(
|
||||
variant: AppLoadingVariant.button,
|
||||
size: 18,
|
||||
trackColor: AppColors.blue200,
|
||||
trackColor: null,
|
||||
)
|
||||
: Text(
|
||||
context.l10n.commonSave,
|
||||
@@ -305,8 +316,8 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: enabled
|
||||
? AppColors.blue600
|
||||
: AppColors.slate400,
|
||||
? colorScheme.primary
|
||||
: colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -319,15 +330,16 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
}
|
||||
|
||||
Widget _buildTabBar() {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
return Container(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(bottom: BorderSide(color: AppColors.border)),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(bottom: BorderSide(color: colorScheme.outlineVariant)),
|
||||
),
|
||||
child: TabBar(
|
||||
controller: _tabController,
|
||||
labelColor: AppColors.blue600,
|
||||
unselectedLabelColor: AppColors.slate600,
|
||||
indicatorColor: AppColors.blue600,
|
||||
labelColor: colorScheme.primary,
|
||||
unselectedLabelColor: colorScheme.onSurfaceVariant,
|
||||
indicatorColor: colorScheme.primary,
|
||||
tabs: [
|
||||
Tab(text: context.l10n.calendarCreateTabBasic),
|
||||
Tab(text: context.l10n.calendarCreateTabAdvanced),
|
||||
@@ -491,15 +503,16 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
bool isOptional = false,
|
||||
DateTime? minTime,
|
||||
}) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
isOptional ? context.l10n.calendarCreateOptionalField(label) : label,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate700,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -515,33 +528,33 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.slate50,
|
||||
color: colorScheme.surfaceContainerLowest,
|
||||
borderRadius: BorderRadius.circular(AppRadius.md),
|
||||
border: Border.all(color: AppColors.borderSecondary),
|
||||
border: Border.all(color: colorScheme.outlineVariant),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icon(
|
||||
LucideIcons.calendar,
|
||||
size: 16,
|
||||
color: AppColors.slate600,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: AppSpacing.sm),
|
||||
Expanded(
|
||||
child: Text(
|
||||
_formatDateTimeLabel(date, time),
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.slate900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icon(
|
||||
LucideIcons.chevronRight,
|
||||
size: 16,
|
||||
color: AppColors.slate400,
|
||||
color: colorScheme.outline,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -568,7 +581,9 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
}) async {
|
||||
final result = await showModalBottomSheet<(DateTime, DateTime)>(
|
||||
context: context,
|
||||
backgroundColor: Colors.transparent,
|
||||
backgroundColor: Theme.of(
|
||||
context,
|
||||
).colorScheme.surface.withValues(alpha: 0),
|
||||
isScrollControlled: true,
|
||||
builder: (context) => DateTimePickerSheet(
|
||||
initialDate: date,
|
||||
@@ -580,6 +595,7 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
}
|
||||
|
||||
Widget _buildColorPicker() {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@@ -588,15 +604,19 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate700,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: defaultColors.map((color) {
|
||||
children: _defaultColors.map((color) {
|
||||
final colorHex =
|
||||
'#${color.toARGB32().toRadixString(16).substring(2).toUpperCase()}';
|
||||
final isSelected = _selectedColor == colorHex;
|
||||
final checkColor =
|
||||
ThemeData.estimateBrightnessForColor(color) == Brightness.dark
|
||||
? Colors.white
|
||||
: Colors.black;
|
||||
return GestureDetector(
|
||||
onTap: () => setState(() => _selectedColor = colorHex),
|
||||
child: Container(
|
||||
@@ -607,11 +627,11 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
color: color,
|
||||
shape: BoxShape.circle,
|
||||
border: isSelected
|
||||
? Border.all(color: AppColors.slate900, width: 2)
|
||||
? Border.all(color: colorScheme.onSurface, width: 2)
|
||||
: null,
|
||||
),
|
||||
child: isSelected
|
||||
? const Icon(Icons.check, size: 16, color: Colors.white)
|
||||
? Icon(Icons.check, size: 16, color: checkColor)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
@@ -622,6 +642,7 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
}
|
||||
|
||||
Widget _buildReminderPicker() {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
String labelOf(int? value) {
|
||||
if (value == null) {
|
||||
return context.l10n.calendarCreateReminderNone;
|
||||
@@ -640,7 +661,7 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate700,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -666,9 +687,9 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(AppSpacing.md),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.slate50,
|
||||
color: colorScheme.surfaceContainerLowest,
|
||||
borderRadius: BorderRadius.circular(AppRadius.md),
|
||||
border: Border.all(color: AppColors.borderSecondary),
|
||||
border: Border.all(color: colorScheme.outlineVariant),
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
@@ -676,17 +697,17 @@ class _CreateEventSheetState extends State<CreateEventSheet>
|
||||
Expanded(
|
||||
child: Text(
|
||||
labelOf(_reminderMinutes),
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: AppColors.slate900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icon(
|
||||
LucideIcons.chevronRight,
|
||||
size: 16,
|
||||
color: AppColors.slate400,
|
||||
color: colorScheme.outline,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:social_app/core/l10n/l10n.dart';
|
||||
|
||||
import '../../../../core/theme/design_tokens.dart';
|
||||
|
||||
class DateTimePickerSheet extends StatefulWidget {
|
||||
final DateTime initialDate;
|
||||
final DateTime initialTime;
|
||||
@@ -132,11 +131,12 @@ class _DateTimePickerSheetState extends State<DateTimePickerSheet> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Container(
|
||||
height: 420,
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColors.white,
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.surface,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
child: Column(
|
||||
@@ -172,7 +172,7 @@ class _DateTimePickerSheetState extends State<DateTimePickerSheet> {
|
||||
l10n.calendarDateTimePickerYearUnit,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.slate600,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
@@ -193,7 +193,7 @@ class _DateTimePickerSheetState extends State<DateTimePickerSheet> {
|
||||
l10n.calendarDateTimePickerMonthUnit,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.slate600,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
@@ -208,7 +208,7 @@ class _DateTimePickerSheetState extends State<DateTimePickerSheet> {
|
||||
l10n.calendarDateTimePickerDayUnit,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: AppColors.slate600,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -217,7 +217,11 @@ class _DateTimePickerSheetState extends State<DateTimePickerSheet> {
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(width: 1, height: 180, color: AppColors.border),
|
||||
Container(
|
||||
width: 1,
|
||||
height: 180,
|
||||
color: colorScheme.outlineVariant,
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Column(
|
||||
@@ -256,12 +260,12 @@ class _DateTimePickerSheetState extends State<DateTimePickerSheet> {
|
||||
itemExtent: 50,
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
Text(
|
||||
' : ',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColors.slate600,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
@@ -289,12 +293,13 @@ class _DateTimePickerSheetState extends State<DateTimePickerSheet> {
|
||||
|
||||
Widget _buildHeader() {
|
||||
final l10n = context.l10n;
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Container(
|
||||
height: 56,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(bottom: BorderSide(color: AppColors.border)),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(bottom: BorderSide(color: colorScheme.outlineVariant)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@@ -304,7 +309,10 @@ class _DateTimePickerSheetState extends State<DateTimePickerSheet> {
|
||||
onTap: () => Navigator.pop(context),
|
||||
child: Text(
|
||||
l10n.commonCancel,
|
||||
style: const TextStyle(fontSize: 17, color: AppColors.slate600),
|
||||
style: TextStyle(
|
||||
fontSize: 17,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
@@ -312,7 +320,7 @@ class _DateTimePickerSheetState extends State<DateTimePickerSheet> {
|
||||
style: TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate900,
|
||||
color: colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
@@ -324,10 +332,10 @@ class _DateTimePickerSheetState extends State<DateTimePickerSheet> {
|
||||
},
|
||||
child: Text(
|
||||
l10n.commonConfirm,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.blue600,
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -337,14 +345,15 @@ class _DateTimePickerSheetState extends State<DateTimePickerSheet> {
|
||||
}
|
||||
|
||||
Widget _buildPickerLabel(String label) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.slate700,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
@@ -358,6 +367,7 @@ class _DateTimePickerSheetState extends State<DateTimePickerSheet> {
|
||||
String Function(int) formatter, {
|
||||
double itemExtent = 40,
|
||||
}) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
return CupertinoPicker(
|
||||
scrollController: controller,
|
||||
itemExtent: itemExtent,
|
||||
@@ -369,7 +379,7 @@ class _DateTimePickerSheetState extends State<DateTimePickerSheet> {
|
||||
decoration: BoxDecoration(
|
||||
border: Border.symmetric(
|
||||
horizontal: BorderSide(
|
||||
color: AppColors.blue100.withValues(alpha: 0.5),
|
||||
color: colorScheme.primary.withValues(alpha: 0.3),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
@@ -379,7 +389,7 @@ class _DateTimePickerSheetState extends State<DateTimePickerSheet> {
|
||||
return Center(
|
||||
child: Text(
|
||||
formatter(items[index]),
|
||||
style: const TextStyle(fontSize: 18, color: AppColors.slate900),
|
||||
style: TextStyle(fontSize: 18, color: colorScheme.onSurface),
|
||||
),
|
||||
);
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user