iT邦幫忙

2026 iThome 鐵人賽

DAY 22
0
AI Engineering

從行程到應援:30 天做出追星實用工具箱系列 第 22 篇

Day 22:將即時翻譯模組整合進「追星工具箱」主介面

  • 分享至 

  • xImage
  •  

一、前言

前面我們完成了音訊處理、Whisper STT、LLM 語音翻譯、飯圈字典、智慧滾動字幕與一鍵摘要,今天要把這個的功能,作為一個 Feature 卡片/頁面,完整嵌入到追星工具箱主介面中!

二、今日任務

Step 1: 建立追星工具箱的主介面

在 lib/features/home/presentation/pages/下建立 toolbox_home_page.dart

import 'package:flutter/material.dart';
// 引用 live_translate 的頁面與功能
import '../../live_translate/presentation/pages/multi_lang_page.dart';

class ToolboxHomePage extends StatelessWidget {
  const ToolboxHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('追星工具箱', style: TextStyle(fontWeight: FontWeight.bold)),
        centerTitle: true,
        elevation: 0,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAlignment: CrossAlignment.start,
          children: [
            // 頂部歡迎語塊
            _buildWelcomeBanner(),
            const SizedBox(height: 20),
            
            const Text(
              '核心功能',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 12),

            // 功能入口 Grid 網格
            GridView.count(
              shrinkWrap: true,
              physics: const NeverScrollableScrollPhysics(),
              crossAxisCount: 2,
              crossAxisSpacing: 12,
              mainAxisSpacing: 12,
              childAspectRatio: 0.85,
              children: [
                // 1. Live Translate 功能入口卡片
                _buildFeatureCard(
                  context: context,
                  title: '即時雙語同傳',
                  subtitle: '聽懂韓日直播/飯圈俚語',
                  tag: 'AI Live',
                  icon: Icons.translate_rounded,
                  gradientColors: [Colors.purple.shade600, Colors.deepPurple.shade900],
                  onTap: () {
                    // 導頁至 Live Translate 模組[cite: 3]
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                        builder: (context) => const MultiLangPage(),
                      ),
                    );
                  },
                ),

                // 2. 預留其他追星工具卡片 (例如:搶票計時器、行程曆)
                _buildFeatureCard(
                  context: context,
                  title: '搶票計時器',
                  subtitle: '精確到毫秒的伺服器時鐘',
                  tag: 'Coming Soon',
                  icon: Icons.timer_outlined,
                  gradientColors: [Colors.blueGrey.shade700, Colors.black87],
                  onTap: () {
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(content: Text('搶票計時器開發中,敬請期待!')),
                    );
                  },
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  /// 頂部橫幅設計
  Widget _buildWelcomeBanner() {
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(20),
      decoration: BoxDecoration(
        gradient: LinearGradient(
          colors: [Colors.pinkAccent.shade200, Colors.purpleAccent.shade400],
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
        ),
        borderRadius: BorderRadius.circular(16),
      ),
      child: const Column(
        crossAlignment: CrossAlignment.start,
        children: [
          Text(
            'Welcome, CARAT / ARMY! ',
            style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w500),
          ),
          SizedBox(height: 6),
          Text(
            '不再怕聽不懂無字幕直播!',
            style: TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold),
          ),
        ],
      ),
    );
  }

  /// 獨立功能卡片 UI
  Widget _buildFeatureCard({
    required BuildContext context,
    required String title,
    required String subtitle,
    required String tag,
    required IconData icon,
    required List<Color> gradientColors,
    required VoidCallback onTap,
  }) {
    return InkWell(
      onTap: onTap,
      borderRadius: BorderRadius.circular(16),
      child: Container(
        padding: const EdgeInsets.all(16),
        decoration: BoxDecoration(
          gradient: LinearGradient(colors: gradientColors, begin: Alignment.topLeft, end: Alignment.bottomRight),
          borderRadius: BorderRadius.circular(16),
          boxShadow: [
            BoxShadow(
              color: gradientColors.first.withOpacity(0.3),
              blurRadius: 8,
              offset: const Offset(0, 4),
            ),
          ],
        ),
        child: Column(
          crossAlignment: CrossAlignment.start,
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Icon(icon, color: Colors.white, size: 32),
                Container(
                  padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                  decoration: BoxDecoration(
                    color: Colors.white24,
                    borderRadius: BorderRadius.circular(12),
                  ),
                  child: Text(
                    tag,
                    style: const TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold),
                  ),
                ),
              ],
            ),
            Column(
              crossAlignment: CrossAlignment.start,
              children: [
                Text(
                  title,
                  style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 4),
                Text(
                  subtitle,
                  style: const TextStyle(color: Colors.white70, fontSize: 11),
                  maxLines: 2,
                  overflow: TextOverflow.ellipsis,
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

Step 2: 在 main.dart 設定首頁入口

打開 lib/main.dart,將首頁設為 ToolboxHomePage

import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'features/home/presentation/pages/toolbox_home_page.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await dotenv.load(fileName: ".env"); // 載入 API Key
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '追星工具箱',
      debugShowCheckedModeBanner: false,
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: const Color(0xFF121212),
        primaryColor: Colors.deepPurple,
      ),
      home: const ToolboxHomePage(),
    );
  }
}

上一篇
Day 21:比較各種 System Prompt 對韓日語俚語翻譯的影響
下一篇
Day 23: 處理翻譯功能使用時造成硬體上的問題
系列文
從行程到應援:30 天做出追星實用工具箱 共 24 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言