CommunityAPP/lib/home_content_page.dart

494 lines
15 KiB
Dart
Raw Normal View History

import 'package:communityapp/bill.dart';
import 'package:communityapp/emergency.dart';
import 'package:communityapp/package.dart';
import 'package:communityapp/visitor.dart';
import 'package:flutter/material.dart';
2025-05-09 16:49:13 +08:00
import 'reapair.dart';
import 'activity.dart';
2025-06-03 13:01:33 +08:00
import 'announcement.dart';
2025-06-03 13:24:09 +08:00
class NotificationIcon extends StatefulWidget {
const NotificationIcon({super.key});
@override
State<NotificationIcon> createState() => _NotificationIconState();
}
class _NotificationIconState extends State<NotificationIcon> {
List<String> _notifications = [];
int get _unreadCount => _notifications.length;
@override
void initState() {
super.initState();
_fetchNotifications(); // 模擬 API 呼叫
}
void _fetchNotifications() async {
// 模擬從 API 取得資料
// await Future.delayed(const Duration(seconds: 1));
setState(() {
_notifications = ['4/15 管理費已出帳', '4/14 社區電梯保養通知', '4/13 新活動報名開始'];
});
}
@override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
children: [
PopupMenuButton<String>(
icon: const Icon(Icons.notifications),
offset: const Offset(0, 40), // 👈 向下移開 40px避免蓋住鈴鐺
onSelected: (value) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('點擊通知:$value')));
},
itemBuilder: (BuildContext context) {
return _notifications.map((String notification) {
return PopupMenuItem<String>(
value: notification,
child: Text(notification),
);
}).toList();
},
),
if (_unreadCount > 0)
Positioned(
right: 4,
top: 4,
child: Container(
padding: const EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10),
),
constraints: const BoxConstraints(minWidth: 16, minHeight: 16),
child: Text(
'$_unreadCount',
style: const TextStyle(color: Colors.white, fontSize: 10),
textAlign: TextAlign.center,
),
),
),
],
);
}
}
class HomeContentPage extends StatelessWidget {
const HomeContentPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('社區通'),
actions: [
Row(
children: [
const Text(
'林小安 您好',
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(width: 8),
2025-06-03 13:24:09 +08:00
NotificationIcon(),
],
),
],
),
body: SingleChildScrollView(
child: Column(
children: [
2025-06-03 13:01:33 +08:00
_announcementSection(context),
_quickMenuSection(context),
_adCarousel(),
_marqueeNotice(),
_activitySection(),
],
),
),
);
}
// 📢 重要公告區塊
2025-06-03 13:01:33 +08:00
static Widget _announcementSection(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
2025-06-03 13:01:33 +08:00
Row(
children: [
const Text(
'📢 重要公告',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const Spacer(),
TextButton(
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (_) => const AnnouncementWrapper(),
);
},
style: OutlinedButton.styleFrom(
foregroundColor: Colors.blue,
side: const BorderSide(color: Colors.blue), // 藍色外框
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20), // 更小的圓角
),
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
), // 更小的內距
minimumSize: const Size(0, 0), // 取消預設最小尺寸限制
tapTargetSize: MaterialTapTargetSize.shrinkWrap, // 點擊範圍不外擴
),
child: const Text(
'更多',
style: TextStyle(fontSize: 14), // 更小字體
),
),
],
),
const SizedBox(height: 8),
2025-06-03 13:01:33 +08:00
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Card(
child: ListTile(
title: const Text('4/20 水塔清洗通知'),
subtitle: const Text('本週六早上9:00至下午3:00進行清洗請提前儲水。'),
),
),
),
],
),
);
}
// 🔧 功能選單區塊
static Widget _quickMenuSection(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'🔧 功能選單',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
GridView.count(
crossAxisCount: 3,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
childAspectRatio: 1,
children: [
_buildQuickButton(context, '報修', 'assets/icons/repair.png'),
2025-06-03 17:50:50 +08:00
_buildQuickButton(context, '包裹', 'assets/icons/package.png'),
_buildQuickButton(context, '訪客', 'assets/icons/visitor.png'),
_buildQuickButton(context, '繳費', 'assets/icons/payment.png'),
_buildQuickButton(context, '社區互動', 'assets/icons/community.png'),
_buildQuickButton(context, '緊急通報', 'assets/icons/emergency.png'),
],
),
],
),
);
}
static Widget _buildQuickButton(
BuildContext context,
String title,
String imgAssetPath,
) {
return GestureDetector(
onTap: () {
2025-05-09 16:49:13 +08:00
switch (title) {
case "報修":
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (_) => const RepairPageWrapper(),
);
case "包裹":
/*Navigator.push(
2025-05-09 16:49:13 +08:00
context,
MaterialPageRoute(builder: (context) => PackagePage()),
);*/
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => PackagePageWrapper(),
);
case "訪客":
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => VisitorPageWrapper(),
);
case "繳費":
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (_) => BillPageWrapper(),
);
case "社區互動":
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (_) => ActivityListPage(),
);
case "緊急通報":
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (_) => EmergencyPage(),
2025-05-09 16:49:13 +08:00
);
default:
}
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(imgAssetPath, width: 40, height: 40, color: Colors.grey),
const SizedBox(height: 8),
Text(title, style: const TextStyle(fontSize: 14)),
],
),
);
}
// 🖼️ 廣告輪播區
static Widget _adCarousel() {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: SizedBox(
height: 180,
child: PageView(
children: [
Image.asset('assets/banners/banner1.png', fit: BoxFit.cover),
Image.asset('assets/banners/banner2.png', fit: BoxFit.cover),
Image.asset('assets/banners/banner3.png', fit: BoxFit.cover),
],
),
),
);
}
// 🏃‍♂️ 跑馬燈公告
static Widget _marqueeNotice() {
return const MarqueeNotice();
}
// 🎉 活動卡片列表
static Widget _activitySection() {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'🎉 最新活動',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
SizedBox(
height: 240,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
_buildActivityCard(
'中秋烤肉趴',
'9/9 晚上6點開烤',
'assets/activities/bbq.png',
),
_buildActivityCard(
'親子日遊園',
'玩具福袋等你拿!',
'assets/activities/family_day.png',
),
_buildActivityCard(
'健康講堂',
'醫師到場解說',
'assets/activities/health.png',
),
_buildActivityCard(
'防災演習',
'模擬火災逃生',
'assets/activities/fire_drill.png',
),
_buildActivityCard(
'社區園遊會',
'免費攤位吃到飽',
'assets/activities/festival.png',
),
],
),
),
],
),
);
}
static Widget _buildActivityCard(
String title,
String subtitle,
String imgAssetPath,
) {
return Container(
width: 180,
margin: const EdgeInsets.only(right: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 5)],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(10)),
child: Image.asset(
imgAssetPath,
height: 120,
width: double.infinity,
fit: BoxFit.cover,
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 4),
Text(
subtitle,
style: const TextStyle(fontSize: 12, color: Colors.black54),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.start, // 左對齊或你要調整的位置
children: [
ElevatedButton(
onPressed: () {
// TODO: 報名事件
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green, // 綠色
fixedSize: const Size(75, 30), // 寬高都 60正方形
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), // 0 圓角
),
),
child: const Text(
'報名',
softWrap: false,
style: TextStyle(color: Colors.white, fontSize: 12),
),
),
const SizedBox(width: 10), // 按鈕間隔
ElevatedButton(
onPressed: () {
// TODO: 查看活動
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // 藍色
fixedSize: const Size(75, 30), // 寬高都 60正方形
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), // 0 圓角
),
),
child: const Text(
'查看',
softWrap: false,
style: TextStyle(color: Colors.white, fontSize: 12),
),
),
],
),
],
),
),
],
),
);
}
}
class MarqueeNotice extends StatefulWidget {
const MarqueeNotice({super.key});
@override
State<MarqueeNotice> createState() => _MarqueeNoticeState();
}
class _MarqueeNoticeState extends State<MarqueeNotice>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
late final Animation<double> _animation;
final String _marqueeText = '公告: 歡迎使用我們的社區通,請多加利用。謝謝您~ 如有問題可洽談管理室';
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 10),
vsync: this,
)..repeat();
_animation = Tween<double>(begin: 1.0, end: -1.0).animate(_controller);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.grey.shade300,
height: 40,
alignment: Alignment.centerLeft,
child: ClipRect(
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return FractionalTranslation(
translation: Offset(_animation.value, 0),
child: child,
);
},
child: Text(
_marqueeText,
style: const TextStyle(fontSize: 16),
overflow: TextOverflow.visible,
softWrap: false,
),
),
),
);
}
}