Flutter开发中使用SQFLite存储API返回用户数据实现免登录功能失败求助
Hey there! Let's work through getting your Flutter app set up to save user data locally with sqflite and enable auto-login. I'll break this down step by step with concrete code examples, and we can troubleshoot specific issues if you hit snags.
Step 1: Add Required Dependencies
First, make sure your pubspec.yaml includes these packages—sqflite for local database, path_provider to find the storage path, and shared_preferences to track login state:
dependencies: flutter: sdk: flutter sqflite: ^2.3.0 path_provider: ^2.1.1 shared_preferences: ^2.2.2
Run flutter pub get to install them.
Step 2: Create a Database Helper Class
This class will handle database initialization, table creation, and all user data operations. It uses a singleton pattern to ensure we only have one database instance:
import 'package:sqflite/sqflite.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; class DatabaseHelper { static final _databaseName = "UserDatabase.db"; static final _databaseVersion = 1; // Table and column names static final table = 'users'; static final columnId = 'id'; static final columnName = 'name'; static final columnAge = 'age'; static final columnEmail = 'email'; // Adjust to your login identifier (e.g., username) static final columnToken = 'auth_token'; // Use this instead of storing plain text passwords! // Singleton instance DatabaseHelper._privateConstructor(); static final DatabaseHelper instance = DatabaseHelper._privateConstructor(); static Database? _database; Future<Database> get database async { if (_database != null) return _database!; _database = await _initDatabase(); return _database!; } // Initialize database _initDatabase() async { final documentsDir = await getApplicationDocumentsDirectory(); final path = join(documentsDir.path, _databaseName); return await openDatabase( path, version: _databaseVersion, onCreate: _onCreate, ); } // Create users table on first run Future _onCreate(Database db, int version) async { await db.execute(''' CREATE TABLE $table ( $columnId INTEGER PRIMARY KEY, $columnName TEXT NOT NULL, $columnAge INTEGER, $columnEmail TEXT UNIQUE NOT NULL, $columnToken TEXT NOT NULL ) '''); } // Insert user data after login/register success Future<int> insertUser(Map<String, dynamic> user) async { final db = await instance.database; // Replace existing user if email matches (for updates) return await db.insert(table, user, conflictAlgorithm: ConflictAlgorithm.replace); } // Fetch user by email (for auto-login) Future<Map<String, dynamic>?> getUserByEmail(String email) async { final db = await instance.database; final results = await db.query( table, where: '$columnEmail = ?', whereArgs: [email], ); return results.isNotEmpty ? results.first : null; } // Delete user data on logout Future<int> deleteUser(int userId) async { final db = await instance.database; return await db.delete(table, where: '$columnId = ?', whereArgs: [userId]); } }
⚠️ Critical Note: Never store plain-text passwords locally. Use an auth token from your API instead (like JWT)—that's what columnToken is for.
Step 3: Handle Login & Save User Data
After your API returns a successful login response, parse the JSON and save it to the local database, then set the login state:
import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:shared_preferences/shared_preferences.dart'; // Example API login call Future<Map<String, dynamic>> login(String email, String password) async { final response = await http.post( Uri.parse('https://your-api-endpoint/login'), body: {'email': email, 'password': password}, ); if (response.statusCode == 200) { return jsonDecode(response.body); } else { throw Exception('Login failed: ${response.body}'); } } // Process login success void handleLoginSuccess(Map<String, dynamic> apiResponse, BuildContext context) async { // Map API data to match our database table final userMap = { DatabaseHelper.columnId: apiResponse['user_id'], DatabaseHelper.columnName: apiResponse['name'], DatabaseHelper.columnAge: apiResponse['age'], DatabaseHelper.columnEmail: apiResponse['email'], DatabaseHelper.columnToken: apiResponse['auth_token'], // From your API }; // Save to local database await DatabaseHelper.instance.insertUser(userMap); // Set login state with shared_preferences final prefs = await SharedPreferences.getInstance(); await prefs.setBool('is_logged_in', true); await prefs.setString('current_user_email', apiResponse['email']); // Navigate to home screen Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => HomeScreen())); }
Step 4: Implement Auto-Login
Check the login state when the app starts, and skip the login screen if the user is already logged in:
void main() async { WidgetsFlutterBinding.ensureInitialized(); final prefs = await SharedPreferences.getInstance(); final isLoggedIn = prefs.getBool('is_logged_in') ?? false; final userEmail = prefs.getString('current_user_email'); runApp(MyApp( initialRoute: isLoggedIn && userEmail != null ? '/home' : '/login', currentUserEmail: userEmail, )); } // In your home screen, load the saved user data class HomeScreen extends StatefulWidget { final String? currentUserEmail; const HomeScreen({super.key, this.currentUserEmail}); @override State<HomeScreen> createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { Map<String, dynamic>? userData; @override void initState() { super.initState(); _loadUserData(); } Future<void> _loadUserData() async { if (widget.currentUserEmail != null) { userData = await DatabaseHelper.instance.getUserByEmail(widget.currentUserEmail!); setState(() {}); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Home')), body: userData != null ? Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Welcome back, ${userData![DatabaseHelper.columnName]}!'), Text('User ID: ${userData![DatabaseHelper.columnId]}'), Text('Age: ${userData![DatabaseHelper.columnAge]}'), ElevatedButton( onPressed: _handleLogout, child: const Text('Logout'), ), ], ) : const Center(child: CircularProgressIndicator()), ); } Future<void> _handleLogout() async { final prefs = await SharedPreferences.getInstance(); await prefs.setBool('is_logged_in', false); await prefs.remove('current_user_email'); await DatabaseHelper.instance.deleteUser(userData![DatabaseHelper.columnId]); Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => LoginScreen())); } }
Common Troubleshooting Tips
If you're still having issues, check these:
- Database initialization errors: Ensure
path_provideris correctly accessing the documents directory (no special permissions needed for Android/iOS). - Insert failures: Verify all required fields are populated, and that the
emailfield is unique (your API should return a unique user identifier). - Auto-login not working: Double-check that
shared_preferencesis saving theis_logged_inflag correctly, and that the email stored matches what's in the database. - Async issues: Make sure all database and shared_preferences calls use
await—forgetting this is a common pitfall.
If you share specific error messages or code snippets that are failing, I can help narrow it down further!
内容的提问来源于stack exchange,提问作者Rishabh

