import 'package:bus_infotainment/backend/live_information.dart'; import 'package:bus_infotainment/backend/modules/commands.dart'; import 'package:bus_infotainment/utils/delegates.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:text_scroll/text_scroll.dart'; import 'package:url_launcher/url_launcher_string.dart'; class pages_Settings extends StatefulWidget { @override State createState() => _pages_SettingsState(); } class _pages_SettingsState extends State { late final Widget _loginPage; @override void initState() { // TODO: implement initState super.initState(); if (!LiveInformation().auth.isAuthenticated()){ _loginPage = _LoginPage( onLogin: () { setState(() {}); }, ); } else { _loginPage = Container( padding: const EdgeInsets.all(8), child: ElevatedButton( onPressed: (){ setState(() {}); LiveInformation().auth.deleteSession(); }, // make the corner radius 4, background color match the theme, and text colour white, fill to width of parent style: ElevatedButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.primary, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4) ), minimumSize: Size(double.infinity, 48) ), child: Text( "Sign out", style: GoogleFonts.interTight( fontSize: 15, fontWeight: FontWeight.w600, color: Colors.white, letterSpacing: 0.5 ) ) ), ); } } @override Widget build(BuildContext context) { return Container( child: SingleChildScrollView( child: Column( children: [ Container( margin: const EdgeInsets.all(8), child: Console() ), Container( height: 2, width: double.infinity, color: Colors.white70, ), Container( padding: const EdgeInsets.all(8), child: _loginPage, ) ], ), ) ); } } enum _LoginType { login, signup } class _LoginPage extends StatefulWidget { _LoginType type = _LoginType.login; final Function() onLogin; _LoginPage({super.key, required this.onLogin, }); @override State<_LoginPage> createState() => _LoginPageState(); } class _LoginPageState extends State<_LoginPage> { @override Widget build(BuildContext context) { return Container( color: Color.fromRGBO(19, 19, 19, 1), padding: const EdgeInsets.symmetric(horizontal: 32), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // Login form widget.type == _LoginType.login ? LoginForm( handleSubmit: (form) { print("Login form submitted"); LiveInformation().auth.createEmailSession( email: form.emailController.text, password: form.passwordController.text ).then((value) { widget.onLogin(); }); }, requestSignup: () { setState(() { widget.type = _LoginType.signup; }); }, ) : SignupForm( handleSubmit: (form) { print("Signup form submitted"); LiveInformation().auth.createUser( displayName: form.dispnameController.text, username: form.usernameController.text, email: form.emailController.text, password: form.passwordController.text ).then((value) { // login LiveInformation().auth.createEmailSession( email: form.emailController.text, password: form.passwordController.text ).then((value) { widget.onLogin(); }); }); }, requestSignin: () { setState(() { widget.type = _LoginType.login; }); }, ), ], ), ); } } class LoginForm extends StatefulWidget { final Function(LoginForm) handleSubmit; final Function() requestSignup; /* TextControllers */ final TextEditingController emailController = TextEditingController(); final TextEditingController passwordController = TextEditingController(); LoginForm({super.key, required this.handleSubmit, required this.requestSignup}); @override State createState() => _LoginFormState(); } class _LoginFormState extends State { @override Widget build(BuildContext context) { return Container( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Sign In", style: GoogleFonts.montserrat( fontSize: 32, fontWeight: FontWeight.bold, color: Colors.white, letterSpacing: -1 ) ), SizedBox(height: 20), PW_TextField( title: "Email", controller: widget.emailController, handleSubmit: (form) { print("Signup form submitted"); widget.handleSubmit(widget); } ), SizedBox(height: 20), PW_TextField( title: "Password", obscure: true, controller: widget.passwordController, handleSubmit: (form) { print("Signup form submitted"); widget.handleSubmit(widget); } ), SizedBox(height: 20), ElevatedButton( onPressed: (){ widget.handleSubmit(widget); }, // make the corner radius 4, background color match the theme, and text colour white, fill to width of parent style: ElevatedButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.primary, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4) ), minimumSize: Size(double.infinity, 48) ), child: Text( "Sign in", style: GoogleFonts.interTight( fontSize: 15, fontWeight: FontWeight.w600, color: Colors.white, letterSpacing: 0.5 ) ) ), SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( onPressed: (){ }, // Make border radius 4, background transparent, and text colour white style: TextButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4) ), backgroundColor: Colors.transparent ), child: Text( "Forgot password?", style: GoogleFonts.interTight( fontSize: 15, fontWeight: FontWeight.w400, color: Colors.grey.shade400, letterSpacing: 0.5 ), ) ), Container( width: 1, height: 24, margin: const EdgeInsets.symmetric(horizontal: 8), color: Colors.grey.shade800, ), TextButton( onPressed: (){ widget.requestSignup(); }, // Make border radius 4, background transparent, and text colour white style: TextButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4) ), backgroundColor: Colors.transparent ), child: Text( "Sign Up", style: GoogleFonts.interTight( fontSize: 15, fontWeight: FontWeight.w400, color: Colors.grey.shade400, letterSpacing: 0.5 ), ) ), ], ), ], ) ); } } class SignupForm extends StatelessWidget { final Function(SignupForm) handleSubmit; final Function() requestSignin; /* TextControllers */ final TextEditingController dispnameController = TextEditingController(); final TextEditingController usernameController = TextEditingController(); final TextEditingController emailController = TextEditingController(); final TextEditingController passwordController = TextEditingController(); final TextEditingController confirmPasswordController = TextEditingController(); SignupForm({super.key, required this.handleSubmit, required this.requestSignin}); @override Widget build(BuildContext context) { return Container( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Sign Up", style: GoogleFonts.montserrat( fontSize: 32, fontWeight: FontWeight.bold, color: Colors.white, letterSpacing: -1 ) ), SizedBox(height: 20), PW_TextField( title: "Display Name", controller: dispnameController, handleSubmit: (form) { print("Signup form submitted"); handleSubmit(this); } ), SizedBox(height: 20), PW_TextField( title: "Username", controller: usernameController, handleSubmit: (form) { print("Signup form submitted"); handleSubmit(this); } ), SizedBox(height: 20), PW_TextField( title: "Email", controller: emailController, handleSubmit: (form) { print("Signup form submitted"); handleSubmit(this); } ), SizedBox(height: 20), PW_TextField( title: "Password", obscure: true, controller: passwordController, handleSubmit: (form) { print("Signup form submitted"); handleSubmit(this); } ), SizedBox(height: 20), PW_TextField( title: "Confirm Password", obscure: true, controller: confirmPasswordController, handleSubmit: (form) { print("Signup form submitted"); handleSubmit(this); } ), SizedBox(height: 20), // Terms and conditions with hyperlink to terms and conditions, with checkbox // use TextSpan // use check box Row( children: [ Checkbox( value: false, onChanged: (value) { print("Checkbox changed to $value"); }, ), Expanded( child: RichText( // wrap text text: TextSpan( children: [ TextSpan( text: "By registering, you agree to our ", style: GoogleFonts.interTight( fontSize: 15, fontWeight: FontWeight.w400, color: Colors.grey.shade400, letterSpacing: 0.5, ), ), TextSpan( text: "Terms and Conditions", style: GoogleFonts.interTight( fontSize: 15, fontWeight: FontWeight.w400, color: Colors.grey.shade400, letterSpacing: 0.5, decoration: TextDecoration.underline, ), recognizer: new TapGestureRecognizer() ..onTap = () { launchUrlString("https://google.co.uk/"); }, ), ], ), ), ), ], ), SizedBox(height: 20), ElevatedButton( onPressed: (){ handleSubmit(this); }, // make the corner radius 4, background color match the theme, and text colour white, fill to width of parent style: ElevatedButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.primary, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4) ), minimumSize: Size(double.infinity, 48) ), child: Text( "Sign up", style: GoogleFonts.interTight( fontSize: 15, fontWeight: FontWeight.w600, color: Colors.white, letterSpacing: 0.5 ) ) ), SizedBox(height: 20), // Already have an account? Sign in Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( "Already have an account? ", style: GoogleFonts.interTight( fontSize: 15, fontWeight: FontWeight.w400, color: Colors.grey.shade400, letterSpacing: 0.5, ), ), TextButton( onPressed: (){ requestSignin(); }, // Make border radius 4, background transparent, and text colour white style: TextButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4) ), backgroundColor: Colors.transparent ), child: Text( "Sign in", style: GoogleFonts.interTight( fontSize: 15, fontWeight: FontWeight.w400, color: Colors.grey.shade400, letterSpacing: 0.5 ), ) ), ], ), ], ) ); } } class PW_TextField extends StatelessWidget { String title = "field"; bool obscure = false; bool longText = false; late TextEditingController controller; Function(String)? handleTapOutside; Function(String)? handleEditingComplete; Function(String)? handleChanged; Function(String)? handleSubmit; PW_TextField({super.key, this.title = "field", required this.controller, this.obscure = false, this.longText = false, this.handleSubmit, this.handleTapOutside, this.handleEditingComplete, this.handleChanged}); @override Widget build(BuildContext context) { // TODO: implement build return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: GoogleFonts.interTight( fontSize: 15, fontWeight: FontWeight.w400, color: Colors.grey.shade300, letterSpacing: 0.1 ) ), SizedBox(height: 4), // field with uniform padding and margin SizedBox( child: TextField( onEditingComplete: () { if (handleEditingComplete != null) { handleEditingComplete!(controller.text); } }, onChanged: (value) { if (handleChanged != null) { handleChanged!(controller.text); } }, onSubmitted: (value) { if (handleSubmit != null) { handleSubmit!(controller.text); } }, onTapOutside: (value) { if (handleTapOutside != null) { handleTapOutside!(controller.text); } }, decoration: InputDecoration( contentPadding: const EdgeInsets.all(12), isDense: true, border: OutlineInputBorder( borderRadius: BorderRadius.circular(4), ), filled: true, // fillColor: STUDIOS_DEFAULT_BACKGROUND_COLOR, hintText: title, hintStyle: GoogleFonts.interTight( fontSize: 15, fontWeight: FontWeight.w400, color: Colors.grey.shade700, letterSpacing: 0.1 ), ), // wrap text minLines: longText ? 3 : 1, maxLines: longText ? null : 1, keyboardType: TextInputType.multiline, obscureText: obscure, style: GoogleFonts.interTight( fontSize: 15, fontWeight: FontWeight.w400, color: Colors.grey.shade300, letterSpacing: 0.1, ), controller: controller, ), ) ], ); } } // Console widget class Console extends StatefulWidget { Console({super.key}); @override State createState() => _ConsoleState(); } class _ConsoleState extends State { ListenerReceipt? _listenerReceipt; @override void initState() { // TODO: implement initState super.initState(); _listenerReceipt = LiveInformation().commandModule.onCommandReceived.addListener((p0) { print("Command received, updating console"); setState(() {}); }); } @override void dispose() { // TODO: implement dispose super.dispose(); } @override Widget build(BuildContext context) { List commands = []; TextEditingController commandController = TextEditingController(); commands.add( Text("Command History:") ); for (int i = 0; i < LiveInformation().commandModule.commandHistory.length; i++){ CommandInfo command = LiveInformation().commandModule.commandHistory[i]; commands.add( TextScroll( command.command, style: GoogleFonts.teko( fontSize: 15, fontWeight: FontWeight.w400, color: Colors.grey.shade300, letterSpacing: 0.1, ), mode: TextScrollMode.bouncing, pauseBetween: const Duration(seconds: 2), pauseOnBounce: const Duration(seconds: 1), ) ); } return Container( decoration: BoxDecoration( border: Border.all(color: Colors.white70, width: 2), color: Colors.black, ), child: Column( children: [ Container( padding: const EdgeInsets.all(8), height: 300, child: ListView( children: commands, ), ), Container( height: 2, width: double.infinity, color: Colors.white70, ), Container( height: 50, padding: const EdgeInsets.all(8), child: TextField( controller: commandController, decoration: const InputDecoration( hintText: ">", ), style: GoogleFonts.teko( height: 1, ), onSubmitted: (value) { commandController.clear(); LiveInformation().commandModule.executeCommand(value); }, ), ) ], ), ); } }