Flutter中基于登录状态控制Drawer内ListView中登录/登出按钮显隐
Hey there! Let's get this sorted out. The key here is to dynamically build your ListView items based on the login state from SharedPreferences, and place the buttons in the correct position (top for login, bottom for logout). Here's how to do it:
Step 1: Handle Async SharedPreferences Fetch
Since accessing SharedPreferences is asynchronous, we'll use a FutureBuilder to wait for the userId before rendering the drawer content. This ensures we don't render anything until we know the login state.
Step 2: Build Dynamic List Items
We'll create a list of widgets for the ListView. If the user is not logged in (userId is null), we add the Login button at the start of the list. If they are logged in, we add the Logout button at the end.
Full Working Code Example
class YourDrawerWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Drawer( child: FutureBuilder<SharedPreferences>( future: SharedPreferences.getInstance(), builder: (context, snapshot) { if (!snapshot.hasData) { // Show loading while waiting for SharedPreferences return Center(child: CircularProgressIndicator()); } final userId = snapshot.data!.getInt('userId'); // Your base drawer items (other links/options in the drawer) final List<Widget> drawerItems = [ // Add your regular drawer items here, e.g.: ListTile( leading: Icon(Icons.home), title: Text('Home'), onTap: () => Navigator.pop(context), ), ListTile( leading: Icon(Icons.settings), title: Text('Settings'), onTap: () { Navigator.pop(context); // Navigate to settings }, ), // Add more items as needed ]; // Insert login/logout button based on state if (userId == null) { // Not logged in: add Login button at the TOP drawerItems.insert(0, _buildLoginButton(context)); } else { // Logged in: add Logout button at the END drawerItems.add(_buildLogoutButton(context)); } return ListView( padding: EdgeInsets.zero, children: drawerItems, ); }, ), ); } // Reusable Login button widget Widget _buildLoginButton(BuildContext context) { return Padding( padding: EdgeInsets.only(top: 10, bottom: 10), child: InkWell( onTap: () { Navigator.push( context, MaterialPageRoute(builder: (context) => LoginandSignupScreen()), ); }, child: Row( children: <Widget>[ Expanded(flex: 2, child: Icon(Icons.card_travel)), Expanded( flex: 10, child: Text('Login', style: TextStyle(fontSize: 15)), ), ], ), ), ); } // Reusable Logout button widget Widget _buildLogoutButton(BuildContext context) { return Padding( padding: EdgeInsets.only(top: 10, bottom: 10), child: InkWell( onTap: () { Provider.of<Auth>(context, listen: false).logout(); // After logout, navigate back to home and refresh the drawer state Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => HomeScreen()), ); }, child: Row( children: <Widget>[ Expanded(flex: 2, child: Icon(Icons.card_travel)), Expanded( flex: 10, child: Text('Logout', style: TextStyle(fontSize: 15)), ), ], ), ), ); } }
Key Notes:
- FutureBuilder: Ensures we wait for SharedPreferences to load before deciding which button to show.
- Reusable Widgets: Extracting login/logout buttons into separate methods keeps the code clean and easy to maintain.
- State Refresh: After logout, using
Navigator.pushReplacementwill rebuild the HomeScreen (and its drawer) so the login button appears immediately without needing manual state updates.
Bonus: Using Provider for Smarter State Management
Since you're already using Provider for Auth, you could listen to the Auth state directly instead of relying on SharedPreferences. This would make the state update automatically when the user logs in/out, without needing to push replacement routes. For example:
// In your drawer widget Consumer<Auth>( builder: (context, auth, child) { final userId = auth.userId; // Assume your Auth provider exposes userId // Build drawer items based on auth.userId here }, )
This way, when auth.logout() is called, the Consumer will rebuild automatically, updating the drawer buttons instantly.
内容的提问来源于stack exchange,提问作者chetan suri

