4. Tela de Desbloqueio (unlock_screen.dart) import 'package:flutter/material.dart'; import 'home_screen.dart'; import 'package:local_auth/local_auth.dart'; class UnlockScreen extends StatefulWidget { @override _UnlockScreenState createState() => _UnlockScreenState(); } class _UnlockScreenState extends State<UnlockScreen> { final TextEditingController passwordController = TextEditingController(); final LocalAuthentication auth = LocalAuthentication(); final String savedPassword = "1234"; // senha exemplo void unlock() { if (passwordController.text == savedPassword) { Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => HomeScreen())); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Senha incorreta!"))); } } Future<void> unlockWithBiometrics() async { bool authenticated = false; try { authenticated = await auth.authenticate( localizedReason: "Use sua biometria para desbloquear"); } catch (e) { print(e); } if (authenticated) { Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => HomeScreen())); } } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Padding( padding: EdgeInsets.all(24), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.lock, size: 80, color: Colors.pink), SizedBox(height: 20), Text("Esse espaço é só seu", style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)), SizedBox(height: 20), TextField( controller: passwordController, decoration: InputDecoration(labelText: "Senha"), obscureText: true, ), SizedBox(height: 20), ElevatedButton( onPressed: unlock, child: Text("Desbloquear")), TextButton( onPressed: unlockWithBiometrics, child: Text("Usar biometria")), SizedBox(height: 20), Text("miau… eu cuido disso pra você", style: TextStyle(fontSize: 16)), ], ), ), ), ); } } --- 5. Tela Diário de Humor (diary_screen.dart) import 'package:flutter/material.dart'; class DiaryScreen extends StatefulWidget { @override _DiaryScreenState createState() => _DiaryScreenState(); } class _DiaryScreenState extends State<DiaryScreen> { String mood = "😐"; bool tremblingLow = false, tremblingMedium = false, tremblingHigh = false; bool ate = false, drank = false, tired = false; final TextEditingController diaryController = TextEditingController(); void saveDiary() { // Aqui você pode salvar no SQLite, Firestore ou SharedPreferences print("Humor: $mood"); print("Mãos tremendo: $tremblingLow/$tremblingMedium/$tremblingHigh"); print("Comi: $ate, Bebi: $drank, Cansada: $tired"); print("Texto: ${diaryController.text}"); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Diário salvo com carinho!"))); } @override Widget build(BuildContext context) { final moods = ["😞", "😐", "🙂", "😄"]; return Scaffold( appBar: AppBar(title: Text("Diário de Humor")), body: Padding( padding: EdgeInsets.all(16), child: ListView( children: [ Text("Data: ${DateTime.now().toLocal()}"), SizedBox(height: 10), Text("Como você se sente hoje?"), Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: moods.map((m) { return GestureDetector( onTap: () => setState(() => mood = m), child: Text(m, style: TextStyle( fontSize: 32, backgroundColor: mood == m ? Colors.pink.shade100 : null)), ); }).toList(), ), SizedBox(height: 20), Text("Mãos tremendo:"), Row( children: [ Checkbox( value: tremblingLow, onChanged: (v) => setState(() => tremblingLow = v!)), Text("Pouco"), Checkbox( value: tremblingMedium, onChanged: (v) => setState(() => tremblingMedium = v!)), Text("Médio"), Checkbox( value: tremblingHigh, onChanged: (v) => setState(() => tremblingHigh = v!)), Text("Nada"), ], ), Row( children: [ Checkbox( value: ate, onChanged: (v) => setState(() => ate = v!)), Text("Comi algo?"), Checkbox( value: drank, onChanged: (v) => setState(() => drank = v!)), Text("Bebi?"), Checkbox( value: tired, onChanged: (v) => setState(() => tired = v!)), Text("Cansada?"), ], ), SizedBox(height: 20), TextField( controller: diaryController, decoration: InputDecoration(labelText: "Hoje eu preciso dizer…"), maxLines: 5, ), SizedBox(height: 20), ElevatedButton(onPressed: saveDiary, child: Text("Salvar com carinho")) ], ), ), ); } } --- 6. Tela Modo Crise (crisis_screen.dart) import 'package:flutter/material.dart'; import 'dart:async'; import 'package:url_launcher/url_launcher.dart'; class CrisisScreen extends StatefulWidget { @override _CrisisScreenState createState() => _CrisisScreenState(); } class _CrisisScreenState extends State<CrisisScreen> { String phrase = "miau… fica"; final phrases = ["miau… fica", "respira comigo", "agora não"]; int index = 0; @override void initState() { super.initState(); Timer.periodic(Duration(seconds: 2), (timer) { setState(() { index = (index + 1) % phrases.length; phrase = phrases[index]; }); }); } void callSomeone() async { final url = Uri.parse("tel:+550000000000"); if (await canLaunchUrl(url)) await launchUrl(url); } void startBreathingExercise() { showDialog( context: context, builder: (_) => AlertDialog( title: Text("Exercício de respiração"), content: Text("Inspire… segure… expire… repita 5 vezes"), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text("Ok")) ], )); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.grey.shade900, body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.pets, size: 120, color: Colors.pink), SizedBox(height: 20), Text(phrase, style: TextStyle(fontSize: 24, color: Colors.white)), SizedBox(height: 40), ElevatedButton(onPressed: callSomeone, child: Text("Ligar para alguém")), ElevatedButton( onPressed: startBreathingExercise, child: Text("Exercício de respiração")), ], ), ), ); } } --- 7. Tela Memórias (memories_screen.dart) import 'package:flutter/material.dart'; class MemoriesScreen extends StatefulWidget { @override _MemoriesScreenState createState() => _MemoriesScreenState(); } class _MemoriesScreenState extends State<MemoriesScreen> { final memories = [ {"image": "https://placekitten.com/200/200", "text": "Dia feliz", "mood": "🙂"}, {"image": "https://placekitten.com/201/201", "text": "Dia difícil", "mood": "😞"}, ]; String filter = "todas"; @override Widget build(BuildContext context) { final filtered = memories .where((m) => filter == "todas" || m["mood"] == filter) .toList(); return Scaffold( appBar: AppBar( title: Text("Memórias"), actions: [ DropdownButton<String>( value: filter, items: ["todas", "🙂", "😞", "😐"] .map((f) => DropdownMenuItem(child: Text(f), value: f)) .toList(), onChanged: (v) => setState(() => filter = v!), ) ], ), body: GridView.builder( padding: EdgeInsets.all(8), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2), itemCount: filtered.length, itemBuilder: (context, index) { final memory = filtered[index]; return GestureDetector( onTap: () => showDialog( context: context, builder: (_) => AlertDialog( title: Text(memory["text"]!), content: Column( mainAxisSize: MainAxisSize.min, children: [ Image.network(memory["image"]!), Text("Emoção: ${memory["mood"]}"), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text("Fechar")) ], )), child: Card( child: Image.network(memory["image"]!, fit: BoxFit.cover), ), ); }, ), ); } } --- 8. Tela Configurações (settings_screen.dart) import 'package:flutter/material.dart'; import 'login_screen.dart'; class SettingsScreen extends StatefulWidget { @override _SettingsScreenState createState() => _SettingsScreenState(); } class _SettingsScreenState extends State<SettingsScreen> { bool notifications = true; String phraseGender = "Feminino"; int autoLock = 5; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Configurações")), body: ListView( children: [ ListTile( leading: Icon(Icons.person), title: Text("Perfil"), onTap: () {}, ), SwitchListTile( title: Text("Notificações"), value: notifications, onChanged: (v) => setState(() => notifications = v), ), ListTile( leading: Icon(Icons.lock), title: Text("Cadeado emocional"), onTap: () {}, ), ListTile( leading: Icon(Icons.support), title: Text("Suporte"), onTap: () {}, ), ListTile( leading: Icon(Icons.logout), title: Text("Logout"), onTap: () => Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => LoginScreen())), ), ], ), ); } }
Atualizei a documentação Flutter com todos os exemplos de código completos que você forneceu. A documentação agora inclui implementações detalhadas de todas as 8 telas principais (Splash, Login, Home, Unlock, Diary, Crisis, Memories e Settings) com código funcional, modelos de dados, e uma comparação técnica entre as versões Next.js e Flutter do aplicativo.
I need to read the file before writing to it. Let me correct this mistake.
Adicionei todas as implementações completas das telas Flutter que você forneceu à documentação. Agora o arquivo FLUTTER_STRUCTURE.md contém exemplos funcionais de todas as 8 telas principais (Unlock, Diary, Crisis, Memories e Settings), complementando os exemplos anteriores de Splash e Login, criando um guia completo para migração do Next.js para Flutter.
home_screen.dart
import 'package:flutter/material.dart';
import 'diary_screen.dart';
import 'crisis_screen.dart';
import 'memories_screen.dart';
import 'settings_screen.dart';
import '../widgets/mascot_widget.dart';
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
String getGreeting() {
final hour = DateTime.now().hour;
if (hour < 12) return "Bom dia, você sobreviveu a hoje 🩷";
if (hour < 18) return "Boa tarde, você sobreviveu a hoje 🩷";
return "Boa noite, você sobreviveu a hoje 🩷";
}
String mood = "😐"; // padrão, pode atualizar conforme diário
void updateMood(String newMood) {
setState(() {
mood = newMood;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Home"),
),
body: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
Text(
getGreeting(),
style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
),
SizedBox(height: 10),
MascotWidget(size: 120, mood: mood), // Mascote reagindo ao humor
SizedBox(height: 30),
Expanded(
child: GridView.count(
crossAxisCount: 2,
mainAxisSpacing: 20,
crossAxisSpacing: 20,
children: [
ElevatedButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: () => DiaryScreen())),
style: ElevatedButton.styleFrom(
padding: EdgeInsets.all(16),
backgroundColor: Colors.pink.shade200),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.book, size: 40),
SizedBox(height: 10),
Text("Diário")
],
),
),
ElevatedButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: () => CrisisScreen())),
style: ElevatedButton.styleFrom(
padding: EdgeInsets.all(16),
backgroundColor: Colors.purple.shade200),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.favorite, size: 40),
SizedBox(height: 10),
Text("Me segura um pouco")
],
),
),
ElevatedButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: () => MemoriesScreen())),
style: ElevatedButton.styleFrom(
padding: EdgeInsets.all(16),
backgroundColor: Colors.orange.shade200),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.photo, size: 40),
SizedBox(height: 10),
Text("Memórias")
],
),
),
ElevatedButton(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: () => SettingsScreen())),
style: ElevatedButton.styleFrom(
padding: EdgeInsets.all(16),
backgroundColor: Colors.green.shade200),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.settings, size: 40),
SizedBox(height: 10),
Text("Configurações")
],
),
),
],
),
),
],
),
),
);
}
}
widget: mascot_widget.dart
Para o mascote reagir ao humor do dia:
import 'package:flutter/material.dart';
class MascotWidget extends StatelessWidget {
final double size;
final String mood; // 😞 😐 🙂 😄
MascotWidget({this.size = 100, this.mood = "😐"});
@override
Widget build(BuildContext context) {
String mascotEmoji;
switch (mood) {
case "😞":
mascotEmoji = "😿"; // triste
break;
case "🙂":
mascotEmoji = "😺"; // feliz
break;
case "😄":
mascotEmoji = "😸"; // super feliz
break;
default:
mascotEmoji = "😼"; // neutro
}
return Text(
mascotEmoji,
style: TextStyle(fontSize: size),
);
}
}
Adicionei os exemplos completos da home_screen.dart e mascot_widget.dart à documentação Flutter. A home_screen implementa navegação com grid de botões para todas as telas principais do app, saudação dinâmica baseada no horário e integração com o mascote que reage ao humor do usuário. O mascot_widget adapta o emoji do mascote baseado no estado emocional registrado, criando uma experiência visual responsiva e empática.
diary_screen.dart (atualizado com onMoodChange)
import 'package:flutter/material.dart';
import '../services/storage_service.dart';
class DiaryScreen extends StatefulWidget {
final Function(String)? onMoodChange;
DiaryScreen({this.onMoodChange});
@override
_DiaryScreenState createState() => _DiaryScreenState();
}
class _DiaryScreenState extends State<DiaryScreen> {
String mood = "😐";
bool tremblingLow = false, tremblingMedium = false, tremblingHigh = false;
bool ate = false, drank = false, tired = false;
final TextEditingController diaryController = TextEditingController();
final List<String> moods = ["😞", "😐", "🙂", "😄"];
void saveDiary() async {
final entry = {
"date": DateTime.now().toIso8601String(),
"mood": mood,
"tremblingLow": tremblingLow,
"tremblingMedium": tremblingMedium,
"tremblingHigh": tremblingHigh,
"ate": ate,
"drank": drank,
"tired": tired,
"text": diaryController.text,
};
await StorageService.saveDiaryEntry(entry);
// Atualiza o mascote no HomeScreen
if (widget.onMoodChange != null) {
widget.onMoodChange!(mood);
}
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text("Diário salvo com carinho!")));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Diário de Humor")),
body: Padding(
padding: EdgeInsets.all(16),
child: ListView(
children: [
Text("Data: ${DateTime.now().toLocal()}"),
SizedBox(height: 10),
Text("Como você se sente hoje?"),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: moods.map((m) {
return GestureDetector(
onTap: () {
setState(() {
mood = m;
});
if (widget.onMoodChange != null) {
widget.onMoodChange!(mood);
}
},
child: Text(
m,
style: TextStyle(
fontSize: 32,
backgroundColor: mood == m
? Colors.pink.shade100
: Colors.transparent),
),
);
}).toList(),
),
SizedBox(height: 20),
Text("Mãos tremendo:"),
Row(
children: [
Checkbox(
value: tremblingLow,
onChanged: (v) => setState(() => tremblingLow = v!)),
Text("Pouco"),
Checkbox(
value: tremblingMedium,
onChanged: (v) => setState(() => tremblingMedium = v!)),
Text("Médio"),
Checkbox(
value: tremblingHigh,
onChanged: (v) => setState(() => tremblingHigh = v!)),
Text("Nada"),
],
),
Row(
children: [
Checkbox(value: ate, onChanged: (v) => setState(() => ate = v!)),
Text("Comi algo?"),
Checkbox(
value: drank, onChanged: (v) => setState(() => drank = v!)),
Text("Bebi?"),
Checkbox(
value: tired, onChanged: (v) => setState(() => tired = v!)),
Text("Cansada?"),
],
),
SizedBox(height: 20),
TextField(
controller: diaryController,
decoration: InputDecoration(labelText: "Hoje eu preciso dizer…"),
maxLines: 5,
),
SizedBox(height: 20),
ElevatedButton(onPressed: saveDiary, child: Text("Salvar com carinho"))
],
),
),
);
}
}
✅ O que mudou:
Adicionado parâmetro onMoodChange no construtor do DiaryScreen.
Ao selecionar o humor, chama widget.onMoodChange!(mood) para atualizar o mascote no HomeScreen.
Ao salvar o diário, também garante que o mascote seja atualizado imediatamente.
Continua salvando os dados localmente usando StorageService.