Skip to content

ajcn2/game-

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 

Repository files navigation

// main.dart import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:google_sign_in/google_sign_in.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart';

final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();

void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(const MyApp()); }

// ===== MyApp ===== class MyApp extends StatelessWidget { const MyApp({super.key});

@override Widget build(BuildContext context) { return MaterialApp( title: 'Game Tracker', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), useMaterial3: true, ), home: const AuthPage(), ); } }

// ===== Autenticação com Google ===== class AuthPage extends StatefulWidget { const AuthPage({super.key}); @override State createState() => _AuthPageState(); }

class _AuthPageState extends State { bool loading = false;

Future signInWithGoogle() async { setState(() => loading = true);

final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
if (googleUser == null) {
  setState(() => loading = false);
  return; // Usuário cancelou
}

final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
final credential = GoogleAuthProvider.credential(
  accessToken: googleAuth.accessToken,
  idToken: googleAuth.idToken,
);

await FirebaseAuth.instance.signInWithCredential(credential);
setState(() => loading = false);

Navigator.pushReplacement(
  context,
  MaterialPageRoute(builder: (context) => const HomePage()),
);

}

@override Widget build(BuildContext context) { return Scaffold( body: Center( child: loading ? const CircularProgressIndicator() : ElevatedButton.icon( icon: const Icon(Icons.login), label: const Text('Entrar com Google'), onPressed: signInWithGoogle, ), ), ); } }

// ===== HomePage ===== class HomePage extends StatefulWidget { const HomePage({super.key});

@override State createState() => _HomePageState(); }

class _HomePageState extends State { final user = FirebaseAuth.instance.currentUser!; final FirebaseFirestore db = FirebaseFirestore.instance; List<Map<String, dynamic>> games = [];

String searchQuery = ''; String filterStatus = 'Todos'; String filterPlatform = 'Todos'; String sortOption = 'Nome';

@override void initState() { super.initState(); loadGames(); }

Future loadGames() async { final snapshot = await db.collection('users').doc(user.uid).collection('games').get(); setState(() { games = snapshot.docs.map((doc) => doc.data()).toList(); }); }

Future addGame(Map<String, dynamic> game) async { final docRef = db.collection('users').doc(user.uid).collection('games').doc(); game['id'] = docRef.id; await docRef.set(game); setState(() { games.add(game); }); }

Future updateGame(Map<String, dynamic> game) async { await db.collection('users').doc(user.uid).collection('games').doc(game['id']).update(game); }

void toggleFavorite(int index) { setState(() { games[index]['favorite'] = !(games[index]['favorite'] ?? false); }); updateGame(games[index]); }

List<Map<String, dynamic>> get filteredAndSortedGames { final filtered = games.where((g) { final matchSearch = g['title'].toLowerCase().contains(searchQuery.toLowerCase()); final matchStatus = filterStatus == 'Todos' || g['status'] == filterStatus; final matchPlatform = filterPlatform == 'Todos' || g['platform'] == filterPlatform; return matchSearch && matchStatus && matchPlatform; }).toList();

filtered.sort((a, b) {
  if ((b['favorite'] ?? false) && !(a['favorite'] ?? false)) return 1;
  if ((a['favorite'] ?? false) && !(b['favorite'] ?? false)) return -1;
  switch (sortOption) {
    case 'Data':
      return DateTime.parse(a['addedAt']).compareTo(DateTime.parse(b['addedAt']));
    case 'Tempo':
      return (a['completionTime'] ?? 0).compareTo(b['completionTime'] ?? 0);
    default:
      return a['title'].compareTo(b['title']);
  }
});

return filtered;

}

@override Widget build(BuildContext context) { final platforms = ['Todos'] + games.map((g) => g['platform'] as String).toSet().toList(); final statuses = ['Todos'] + games.map((g) => g['status'] as String).toSet().toList(); final sortOptions = ['Nome', 'Data', 'Tempo'];

return Scaffold(
  appBar: AppBar(
    title: const Text('Minha Biblioteca de Jogos'),
    actions: [
      IconButton(
        icon: const Icon(Icons.logout),
        onPressed: () async {
          await FirebaseAuth.instance.signOut();
          await GoogleSignIn().signOut();
          Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const AuthPage()));
        },
      ),
      IconButton(
        icon: const Icon(Icons.bar_chart),
        onPressed: () {
          Navigator.push(context, MaterialPageRoute(builder: (context) => StatsPage(games: games)));
        },
      ),
      IconButton(
        icon: const Icon(Icons.new_releases),
        onPressed: () {
          Navigator.push(context, MaterialPageRoute(builder: (context) => const UpcomingGamesPage()));
        },
      ),
      IconButton(
        icon: const Icon(Icons.add),
        onPressed: () async {
          final newGame = await Navigator.push(
              context,
              MaterialPageRoute(
                  builder: (context) => AddGamePage(
                        onAdd: addGame,
                      )));
        },
      ),
    ],
  ),
  body: Column(
    children: [
      Padding(
        padding: const EdgeInsets.all(8.0),
        child: TextField(
          decoration: const InputDecoration(
            labelText: 'Pesquisar jogos',
            prefixIcon: Icon(Icons.search),
            border: OutlineInputBorder(),
          ),
          onChanged: (value) => setState(() => searchQuery = value),
        ),
      ),
      Padding(
        padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
        child: Row(
          children: [
            Expanded(
              child: DropdownButtonFormField<String>(
                value: filterStatus,
                items: statuses.map((s) => DropdownMenuItem(value: s, child: Text(s))).toList(),
                onChanged: (value) => setState(() => filterStatus = value!),
                decoration: const InputDecoration(labelText: 'Status', border: OutlineInputBorder()),
              ),
            ),
            const SizedBox(width: 8),
            Expanded(
              child: DropdownButtonFormField<String>(
                value: filterPlatform,
                items: platforms.map((p) => DropdownMenuItem(value: p, child: Text(p))).toList(),
                onChanged: (value) => setState(() => filterPlatform = value!),
                decoration: const InputDecoration(labelText: 'Plataforma', border: OutlineInputBorder()),
              ),
            ),
            const SizedBox(width: 8),
            Expanded(
              child: DropdownButtonFormField<String>(
                value: sortOption,
                items: sortOptions.map((s) => DropdownMenuItem(value: s, child: Text(s))).toList(),
                onChanged: (value) => setState(() => sortOption = value!),
                decoration: const InputDecoration(labelText: 'Ordenar por', border: OutlineInputBorder()),
              ),
            ),
          ],
        ),
      ),
      Expanded(
        child: filteredAndSortedGames.isEmpty
            ? const Center(child: Text('Nenhum jogo encontrado'))
            : GridView.builder(
                padding: const EdgeInsets.all(12),
                gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: 2,
                  childAspectRatio: 0.75,
                  crossAxisSpacing: 10,
                  mainAxisSpacing: 10,
                ),
                itemCount: filteredAndSortedGames.length,
                itemBuilder: (context, index) {
                  final game = filteredAndSortedGames[index];
                  return Card(
                    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
                    elevation: 4,
                    child: Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Expanded(
                            child: Container(
                              decoration: BoxDecoration(
                                borderRadius: BorderRadius.circular(12),
                                color: Colors.deepPurple.shade100,
                              ),
                              child: const Center(child: Icon(Icons.videogame_asset, size: 50)),
                            ),
                          ),
                          const SizedBox(height: 8),
                          Text(game['title'] ?? '', maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle(fontWeight: FontWeight.bold)),
                          Text(game['platform'] ?? '', style: TextStyle(color: Colors.grey.shade600, fontSize: 12)),
                          Text('Status: ${game['status'] ?? ''}', style: const TextStyle(fontSize: 12)),
                          Align(
                            alignment: Alignment.centerRight,
                            child: IconButton(
                              icon: Icon(game['favorite'] == true ? Icons.star : Icons.star_border, color: Colors.amber),
                              onPressed: () => toggleFavorite(games.indexOf(game)),
                            ),
                          ),
                        ],
                      ),
                    ),
                  );
                },
              ),
      ),
    ],
  ),
);

} }

// ===== AddGamePage ===== class AddGamePage extends StatefulWidget { final Function(Map<String, dynamic>) onAdd;

const AddGamePage({super.key, required this.onAdd});

@override State createState() => _AddGamePageState(); }

class _AddGamePageState extends State { final _formKey = GlobalKey(); final TextEditingController titleController = TextEditingController(); final TextEditingController platformController = TextEditingController(); String status = 'Quero jogar'; final TextEditingController completionController = TextEditingController(); final TextEditingController releaseController = TextEditingController();

@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Adicionar Jogo')), body: Padding( padding: const EdgeInsets.all(12.0), child: Form( key: _formKey, child: ListView( children: [ TextFormField(controller: titleController, decoration: const InputDecoration(labelText: 'Título'), validator: (v) => v!.isEmpty ? 'Preencha o título' : null), TextFormField(controller: platformController, decoration: const InputDecoration(labelText: 'Plataforma'), validator: (v) => v!.isEmpty ? 'Preencha a plataforma' : null), DropdownButtonFormField( value: status, items: ['Quero jogar', 'Jogando', 'Zerado'].map((s) => DropdownMenuItem(value: s, child: Text(s))).toList(), onChanged: (v) => setState(() => status = v!), decoration: const InputDecoration(labelText: 'Status'), ), TextFormField(controller: completionController, decoration: const InputDecoration(labelText: 'Tempo de conclusão (h)'), keyboardType: TextInputType.number), TextFormField(controller: releaseController, decoration: const InputDecoration(labelText: 'Data de lançamento (AAAA-MM-DD)')), const SizedBox(height: 20), ElevatedButton( onPressed: () { if (_formKey.currentState!.validate()) { widget.onAdd({ 'title': titleController.text, 'platform': platformController.text, 'status': status, 'completionTime': int.tryParse(completionController.text) ?? 0, 'releaseDate': releaseController.text, 'addedAt': DateTime.now().toIso8601String(), 'favorite': false, }); Navigator.pop(context); } }, child: const Text('Adicionar')), ], ), ), ), ); } }

// ===== UpcomingGamesPage ===== class UpcomingGamesPage extends StatefulWidget { const UpcomingGamesPage({super.key});

@override State createState() => _UpcomingGamesPageState(); }

class _UpcomingGamesPageState extends State { final user = FirebaseAuth.instance.currentUser!; final FirebaseFirestore db = FirebaseFirestore.instance; List<Map<String, dynamic>> upcomingGames = [];

@override void initState() { super.initState(); loadUpcomingGames(); }

Future loadUpcomingGames() async { final now = DateTime.now(); final snapshot = await db.collection('users').doc(user.uid).collection('games').get(); setState(() { upcomingGames = snapshot.docs .map((doc) => doc.data()) .where((g) { final dateStr = g['releaseDate']; if (dateStr == null) return false; final release = DateTime.tryParse(dateStr); return release != null && release.isAfter(now); }) .toList(); }); }

@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Lançamentos Futuros')), body: upcomingGames.isEmpty ? const Center(child: Text('Nenhum lançamento futuro')) : ListView.builder( itemCount: upcomingGames.length, itemBuilder: (context, index) { final game = upcomingGames[index]; return ListTile( leading: const Icon(Icons.videogame_asset), title: Text(game['title'] ?? ''), subtitle: Text('Plataforma: ${game['platform'] ?? ''}\nLançamento: ${game['releaseDate'] ?? ''}'), ); }, ), ); } }

// ===== StatsPage ===== class StatsPage extends StatelessWidget { final List<Map<String, dynamic>> games;

const StatsPage({super.key, required this.games});

@override Widget build(BuildContext context) { final statusCounts = <String, int>{}; final platformCounts = <String, int>{};

for (var g in games) {
  final status = g['status'] ?? 'Desconhecido';
  final platform = g['platform'] ?? 'Desconhecido';
  statusCounts[status] = (statusCounts[status] ?? 0) + 1;
  platformCounts[platform] = (platformCounts[platform] ?? 0) + 1;
}

return Scaffold(
  appBar: AppBar(title: const Text('Estatísticas')),
  body: Padding(
    padding: const EdgeInsets.all(12.0),
    child: Column(
      children: [
        const Text('Jogos por Status', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
        SizedBox(height: 200, child: PieChart(PieChartData(sections: statusCounts.entries.map((e) {
          return PieChartSectionData(value: e.value.toDouble(), title: '${e.key} (${e.value})', radius: 50);
        }).toList()))),
        const SizedBox(height: 20),
        const Text('Jogos por Plataforma', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
        SizedBox(height: 200, child: PieChart(PieChartData(sections: platformCounts.entries.map((e) {
          return PieChartSectionData(value: e.value.toDouble(), title: '${e.key} (${e.value})', radius: 50);
        }).toList()))),
      ],
    ),
  ),
);

} }

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published