Home > Back-end >  TextFormField causing whole Flutter app to refresh: StreamBuilder issue?
TextFormField causing whole Flutter app to refresh: StreamBuilder issue?

Time:05-25

I am currently making a room booking app. I have implemented the google authentication portion using SteamBuilder. However, once logged into the app, I have a Form to fill in the details - which I would eventually want to save to Cloud Firestore.

However, now I'm facing the issue that every time I click on the TextFormField widget it refreshes my entire screen. Here is the structure for my code and the code subsequently.

  1. main.dart - Main file with routes
  2. home_page.dart - Basically the file which consists of the Stream. Determines if user if logged in and whether to bring to login page or into the app.
  3. login_page.dart - The page to log in with GoogleAuth
  4. booking_pages.dart - Main file for the app's backbone. Consist of the scaffold which can navigate between the 2 main pages in the app
  5. book_room.dart - The page which has the Form and TextFormFields inside
  6. my_profile.dart - Merely displays user profile from google.

To test, I even included a TextFormField inside of my_profile.dart and realized that it also refreshes and brings me to book_room.dart immediately.

Having read about this, it seems to be something to do with Streams but I can't seem to understand how to fix this. All my code is below. Any help is appreciated thanks!

main.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:room_booking/google_sign_in.dart';
import 'package:room_booking/pages/booking_pages.dart';
import 'package:room_booking/pages/my_profile.dart';
import 'package:room_booking/pages/book_room.dart';
import '../pages/home_page.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';

const MaterialColor primaryBlack = MaterialColor(
  _blackPrimaryValue,
  <int, Color>{
    50: Color(0xFF000000),
    100: Color(0xFF000000),
    200: Color(0xFF000000),
    300: Color(0xFF000000),
    400: Color(0xFF000000),
    500: Color(_blackPrimaryValue),
    600: Color(0xFF000000),
    700: Color(0xFF000000),
    800: Color(0xFF000000),
    900: Color(0xFF000000),
  },
);
const int _blackPrimaryValue = 0xFF000000;
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  runApp(
    ChangeNotifierProvider(
      create: (context) => GoogleSignInProvider(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  MyApp({Key? key}) : super(key: key);
  final storage = FirebaseStorage.instance;

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: primaryBlack),
      initialRoute: 'home',
      routes: {
        'home': (context) => HomePage(),
        'bookinghome': (context) => BookingPages(),
        'book': (context) => BookRoom(),
        'myprofile': (context) => MyProfile(),
      },
    );
  }
}

home_page.dart

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:room_booking/main.dart';
import 'package:room_booking/pages/booking_pages.dart';
import 'package:room_booking/pages/my_profile.dart';
import 'package:room_booking/pages/book_room.dart';
import 'package:room_booking/pages/login_page.dart';

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

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

class _HomePageState extends State<HomePage> {
  double? _deviceHeight, _deviceWidth;
  String? _description;

  @override
  Widget build(BuildContext context) {
    _deviceHeight = MediaQuery.of(context).size.height;
    _deviceWidth = MediaQuery.of(context).size.width;
    return Scaffold(
      body: StreamBuilder(
        stream: FirebaseAuth.instance.authStateChanges(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          } else if (snapshot.hasData) {
            return BookingPages();
          } else if (snapshot.hasError) {
            return const Center(
              child: Text("Something went wrong"),
            );
          } else {
            return LoginPage();
          }
        },
      ),
    );
  }
}

login_page.dart

import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart';
import 'package:room_booking/google_sign_in.dart';
import 'package:room_booking/main.dart';

class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  double? _deviceHeight, _deviceWidth;

  @override
  Widget build(BuildContext context) {
    _deviceHeight = MediaQuery.of(context).size.height;
    _deviceWidth = MediaQuery.of(context).size.width;
    return Scaffold(
      backgroundColor: Colors.grey[900],
      body: Container(
        padding: EdgeInsets.symmetric(
            horizontal: _deviceWidth! * 0.1, vertical: _deviceHeight! * 0.05),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          mainAxisSize: MainAxisSize.max,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Container(
              padding: EdgeInsets.fromLTRB(0, _deviceHeight! * 0.2, 0, 0),
              child: Column(
                children: const [
                  Text(
                    "Welcome...",
                    style: TextStyle(
                        fontSize: 35,
                        fontWeight: FontWeight.bold,
                        color: Colors.white),
                  ),
                  Text(
                    "   to Room Booking",
                    style: TextStyle(
                        fontSize: 25,
                        fontWeight: FontWeight.bold,
                        color: Colors.white54),
                  ),
                ],
              ),
            ),
            Container(
              padding: EdgeInsets.fromLTRB(
                  0, _deviceHeight! * 0.1, 0, _deviceHeight! * 0.2),
              child: const Center(
                child: Icon(
                  Icons.book,
                  color: Colors.white,
                  size: 70,
                ),
              ),
            ),
            ElevatedButton.icon(
                style: ElevatedButton.styleFrom(
                    primary: Colors.white,
                    onPrimary: Colors.black,
                    minimumSize: Size(double.infinity, 50)),
                onPressed: () {
                  final provider =
                      Provider.of<GoogleSignInProvider>(context, listen: false);
                  provider.googleLogin();
                },
                icon: const FaIcon(
                  FontAwesomeIcons.google,
                  color: Colors.red,
                ),
                label: const Text("Sign in with Google"))
          ],
        ),
      ),
    );
  }
}

bookings_page.dart

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:room_booking/google_sign_in.dart';
import 'package:room_booking/main.dart';
import 'package:room_booking/pages/book_room.dart';
import 'package:room_booking/pages/my_profile.dart';

class BookingPages extends StatefulWidget {
  const BookingPages({Key? key}) : super(key: key);

  @override
  State<BookingPages> createState() => _BookingPagesState();
}

class _BookingPagesState extends State<BookingPages> {
  int _currentPage = 0;
  final List<Widget> _pages = [
    BookRoom(),
    MyProfile(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(
          "Room Booking",
        ),
        centerTitle: true,
        actions: [
          TextButton(
            onPressed: () {
              final provider =
                  Provider.of<GoogleSignInProvider>(context, listen: false);
              provider.logout();
            },
            child: const Text(
              "Logout",
              style: TextStyle(color: Colors.blue),
            ),
          )
        ],
      ),
      body: _pages[_currentPage],
      bottomNavigationBar: _bottomNavigationBar(),
    );
  }

  Widget _bottomNavigationBar() {
    final user = FirebaseAuth.instance.currentUser!;
    return BottomNavigationBar(
      currentIndex: _currentPage,
      unselectedItemColor: Colors.white,
      selectedItemColor: Colors.blue,
      backgroundColor: primaryBlack,
      onTap: (_int) {
        setState(
          () {
            _currentPage = _int;
          },
        );
      },
      items: [
        const BottomNavigationBarItem(
          label: "Book",
          icon: Icon(Icons.book_online),
        ),
        BottomNavigationBarItem(
          label: "My Profile",
          icon: ImageIcon(
            NetworkImage(user.photoURL!),
          ),
        ),
      ],
    );
  }
}

book_room.dart

import 'package:flutter/material.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:path/path.dart' as path;

class BookRoom extends StatefulWidget {
  const BookRoom({Key? key}) : super(key: key);

  @override
  State<BookRoom> createState() => _SelectRoomState();
}

class _SelectRoomState extends State<BookRoom> {
  final List<Widget> _pages = [];
  final Map<String, String?> _fields = {
    "description": null,
    "room": null,
    "datetime": null,
    "name": null,
    "unit": null,
    "purpose": null,
  };

  final storage = FirebaseStorage.instance;
  final storageRef = FirebaseStorage.instance.ref();

  //Form Key for the Form
  final GlobalKey<FormState> _bookingFormKey = GlobalKey<FormState>();

  double? _deviceHeight, _deviceWidth;
  String? _description, _room, _datetime, _name, _unit, _purpose;

  @override
  Widget build(BuildContext context) {
    _deviceHeight = MediaQuery.of(context).size.height;
    _deviceWidth = MediaQuery.of(context).size.width;
    return Scaffold(
      body: _newBooking(),
    );
  }

  Widget _newBooking() {
    return Container(
      padding: EdgeInsets.symmetric(
          horizontal: _deviceWidth! * 0.05, vertical: _deviceHeight! * 0.05),
      child: SingleChildScrollView(
        child: Form(
          key: _bookingFormKey,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            mainAxisSize: MainAxisSize.max,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              _selectionWidget("Select Room", "room"),
              _selectionWidget("Select Date/Time", "datetime"),
              _selectionWidget("Name of personnel booking", "name"),
              _selectionWidget("Unit", "unit"),
              _selectionWidget("Purpose", "purpose"),
              (_fields["room"] ?? "").isEmpty ? Text("yes") : Text("no"),
            ],
          ),
        ),
      ),
    );
  }

  Widget _selectionWidget(description, variable) {
    return Container(
      height: _deviceHeight! * 0.1,
      child: TextFormField(
        decoration: InputDecoration(
          labelText: description,
          border: const OutlineInputBorder(),
          errorBorder: const OutlineInputBorder(
            borderSide: BorderSide(color: Colors.red, width: 5),
          ),
        ),
        onChanged: (value) {
          print(value);
          setState(
            () {
              _fields[variable] = value;
            },
          );
        },
      ),
    );
  }
}

my_profile.dart

import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

class MyProfile extends StatefulWidget {
  const MyProfile({Key? key}) : super(key: key);

  @override
  State<MyProfile> createState() => _MyProfileState();
}

class _MyProfileState extends State<MyProfile> {
  double? _deviceHeight, _deviceWidth;
  final GlobalKey<FormState> _profileFormKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    _deviceHeight = MediaQuery.of(context).size.height;
    _deviceWidth = MediaQuery.of(context).size.width;
    final user = FirebaseAuth.instance.currentUser!;

    return Scaffold(
      body: Container(
        padding: EdgeInsets.symmetric(
            horizontal: _deviceWidth! * 0.05, vertical: _deviceHeight! * 0.05),
        child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            mainAxisSize: MainAxisSize.max,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                children: [
                  CircleAvatar(
                    radius: 40,
                    backgroundImage: NetworkImage(user.photoURL!),
                  ),
                  Container(
                    width: _deviceWidth! * 0.07,
                  ),
                  Text(
                    user.displayName!,
                    style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
                  )
                ],
              ),
              Form(
                key: _profileFormKey,
                child: TextFormField(initialValue: "Hi"),
              )
            ]),
      ),
    );
  }
}

CodePudding user response:

You can't use setState() inside method onChanged() of TextFormField in class BookRoom

All class BookRoom in file book_room.dart will rebuild

Try Extract Widget TextFormField to new StatefulWidget() class in different file

CodePudding user response:

My humble advice is to get a good practice of using a stateless/ful widget over a function flutter would not come up with them if you just could solve problems with functions

In short: better performance and optimization

If you have time to learn more check this links: this and this

I think swapping widget functions in the root of your scaffold will be sufficient to solve refresh

  • Related