You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Flutter登录页SharedPreferences自动登录问题及代码修改指导

Hey there! As a fellow Flutter developer, I'll walk you through exactly how to implement auto-login using SharedPreferences and Firebase for your login screen, plus fix common pitfalls in your existing code. Let's dive in!

Step 1: Update Your Dependencies

First, make sure you have the required packages in your pubspec.yaml file. Add these lines under the dependencies section:

dependencies:
  flutter:
    sdk: flutter
  firebase_auth: ^4.12.0 # Use the latest available version
  shared_preferences: ^2.2.2 # Use the latest available version

Run flutter pub get in your terminal to install the packages.

Step 2: Initialize Firebase & SharedPreferences on App Startup

In your main.dart, we'll initialize Firebase first, then check for saved login state before deciding which screen to show:

import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'login_screen.dart';
import 'home_screen.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // Initialize Firebase
  await Firebase.initializeApp();
  // Get SharedPreferences instance
  final prefs = await SharedPreferences.getInstance();
  // Check if user has previously logged in
  final isLoggedIn = prefs.getBool('isLoggedIn') ?? false;

  runApp(MyApp(isLoggedIn: isLoggedIn));
}

class MyApp extends StatelessWidget {
  final bool isLoggedIn;

  const MyApp({super.key, required this.isLoggedIn});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Auto Login Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      // Skip login screen if user is already authenticated
      home: isLoggedIn ? const HomeScreen() : const LoginScreen(),
    );
  }
}
Step 3: Modify Your Login Screen Logic

Let's update your login screen to save user state after successful Firebase authentication, plus fix common issues like loading states and error handling:

First, add these variables to your login screen's state class:

late final TextEditingController _emailController;
late final TextEditingController _passwordController;
bool _isLoading = false;
late final SharedPreferences _prefs;

Initialize them in initState (we'll also pre-fill the email if it was saved):

@override
void initState() {
  super.initState();
  _emailController = TextEditingController();
  _passwordController = TextEditingController();
  // Initialize SharedPreferences
  _initPrefs();
}

Future<void> _initPrefs() async {
  _prefs = await SharedPreferences.getInstance();
  // Pre-fill email if saved from previous login
  final savedEmail = _prefs.getString('userEmail');
  if (savedEmail != null) {
    _emailController.text = savedEmail;
  }
}

Update your login function to save state after successful authentication:

Future<void> _login() async {
  setState(() {
    _isLoading = true;
  });

  try {
    // Firebase email/password login
    final userCredential = await FirebaseAuth.instance.signInWithEmailAndPassword(
      email: _emailController.text.trim(),
      password: _passwordController.text.trim(),
    );

    if (userCredential.user != null) {
      // Save login state and user email to SharedPreferences
      await _prefs.setBool('isLoggedIn', true);
      await _prefs.setString('userEmail', _emailController.text.trim());

      // Navigate to home screen (replace login screen to prevent going back)
      if (mounted) {
        Navigator.pushReplacement(
          context,
          MaterialPageRoute(builder: (context) => const HomeScreen()),
        );
      }
    }
  } on FirebaseAuthException catch (e) {
    // Show error message for common issues like wrong password
    if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(e.message ?? 'Login failed. Please try again.')),
      );
    }
  } finally {
    setState(() {
      _isLoading = false;
    });
  }
}

Update your login button to handle the loading state (prevents duplicate taps):

ElevatedButton(
  onPressed: _isLoading ? null : _login,
  child: _isLoading 
      ? const CircularProgressIndicator(color: Colors.white) 
      : const Text('Login'),
)

Don't forget to clean up controllers when the widget is disposed:

@override
void dispose() {
  _emailController.dispose();
  _passwordController.dispose();
  super.dispose();
}
Step 4: Add Logout Function to Home Screen

To disable auto-login after logout, add this function to your home screen:

Future<void> _logout() async {
  // Sign out from Firebase
  await FirebaseAuth.instance.signOut();
  // Clear saved login state from SharedPreferences
  final prefs = await SharedPreferences.getInstance();
  await prefs.setBool('isLoggedIn', false);
  await prefs.remove('userEmail');

  // Navigate back to login screen
  if (mounted) {
    Navigator.pushReplacement(
      context,
      MaterialPageRoute(builder: (context) => const LoginScreen()),
    );
  }
}

You can trigger this from a button in your home screen:

IconButton(
  icon: const Icon(Icons.logout),
  onPressed: _logout,
)
Key Fixes & Best Practices
  • Never store plain text passwords: We only save a login state flag and user email (not password) since Firebase handles authentication securely.
  • Handle async operations safely: Always wrap Firebase calls in try/catch blocks and check if the widget is mounted before updating UI to avoid crashes.
  • Loading state management: Disable the login button during authentication to prevent duplicate login attempts.
  • Persistent sessions: Firebase Auth already persists user sessions by default, but using SharedPreferences lets us skip the login screen immediately on app startup.

内容的提问来源于stack exchange,提问作者Andrey Belichenko

火山引擎 最新活动