So, you're building apps with Flutter? That's awesome! Flutter is a fantastic tool for creating beautiful apps for phones, computers, and the web using just one code.
But how do you go from just making an app to making a great app? It's like cooking: anyone can follow a recipe, but a great chef knows the little secrets that make the food amazing. This guide will share those secrets for Flutter. We'll look at some simple "Dos" and "Don'ts" to help you write code that is clean, fast, and easy to work with.
1. Do: Break Down Your Screen into Small Pieces
The Don't: Don't put the code for your entire screen in one giant piece. It gets messy, hard to read, and if you change one tiny thing, the whole screen has to reload, which is slow.
// Don't do this! It's one giant piece of code.
class ProfileScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
CircleAvatar(radius: 50),
Text('John Doe', style: TextStyle(fontSize: 24)),
Text('About Me: I love Flutter!'),
Text('Location: Kigali, Rwanda'),
ElevatedButton(onPressed: () {}, child: Text('Edit')),
],
),
);
}
}
The Do: Break your screen into smaller, reusable widgets. This keeps your code clean and helps your app run faster because Flutter only reloads the small piece that changes.
// Do this! It's organized into small pieces.
class ProfileScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
ProfileHeader(),
UserDetails(),
ActionButtons(),
],
),
);
}
}
// Each piece is separate and reusable
class ProfileHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
CircleAvatar(radius: 50),
Text('John Doe', style: TextStyle(fontSize: 24)),
],
);
}
}
2. Do: Handle Your App's Memory with Care
Imagine your app has a "memory" or "state," like the score in a game or a list of items in a shopping cart. When that memory changes, your screen needs to update.
The Don't: Don't just call setState()
every time something changes. setState() is like a panic button that tells your entire screen, "Everyone, update now!" Even parts of the screen that didn't change have to do work, which is inefficient.
The Do: Use a proper state management tool (like Provider or Riverpod). Think of it as a smart manager. Instead of yelling at everyone, the manager just taps the specific worker (widget) on the shoulder that needs to update. This is much more efficient.
Here's a simple idea of how Provider works for a counter app:
// This class holds the count data.
class Counter extends ChangeNotifier {
int _value = 0;
int get value => _value;
void increment() {
_value++;
notifyListeners(); // This tells the smart manager to update only what's needed.
}
}
// In the UI, you just "watch" for changes.
// Only this Text widget will update when the count changes!
Text('The count is: ${context.watch<Counter>().value}')
3. Do: Use const to Give Your App a Break
Some parts of your app never, ever change. For example, a color, a title text, or the padding around a button.
The Don't: Don't let Flutter rebuild things that are never going to change. It's a waste of your phone's battery and processing power.
The Do: Add the word const
in front of any widget that is constant. This is a signal to Flutter that says, "Hey, this piece is finished. You never have to build it again." It's one of the easiest ways to make your app faster.
// Don't do this:
Padding(
padding: EdgeInsets.all(16.0),
child: Text('Hello, Kigali!'),
);
// Do this! Add `const`.
const Padding(
padding: EdgeInsets.all(16.0),
child: Text('Hello, Kigali!'),
);
It's a small change that makes a big difference.
4. Do: Make Your App Look Good on All Screens
Your app will be used on tiny phones, huge tablets, and everything in between. You need to make sure it looks good on all of them!
The Don't: Don't hardcode sizes using fixed numbers. When you write SizedBox(height: 20)
, that 20 pixels might look perfect on your phone, but it could be too tiny on a tablet or too big on a small phone.
// Don't: Fixed sizes don't adapt
Column(
children: [
Text('Welcome!'),
SizedBox(height: 20), // Always 20 pixels
SizedBox(height: 40), // Always 40 pixels
ElevatedButton(onPressed: () {}, child: Text('Click')),
],
);
The Do: Use MediaQuery
to make your sizes responsive. Think of MediaQuery as a measuring tape that tells you how big the screen is, so you can adjust accordingly.
// Do: Responsive sizes
Column(
children: [
Text('Welcome!'),
SizedBox(height: MediaQuery.of(context).size.height * 0.02), // 2% of screen
SizedBox(height: MediaQuery.of(context).size.height * 0.05), // 5% of screen
ElevatedButton(onPressed: () {}, child: Text('Click')),
],
);
// Pro tip: Create a helper
double screenHeight(BuildContext context) => MediaQuery.of(context).size.height;
// Then use: SizedBox(height: screenHeight(context) * 0.02)
5. Do: Keep Your Styles Consistent with Themes
Imagine if every page in a book used a different font, size, and color. It would be chaos! The same goes for your app.
The Don't: Don't write the same style code over and over again throughout your app. This is like copying and pasting – if you want to change the color later, you'll have to hunt down every single place you used it.
// Don't: Inline styles everywhere
Column(
children: [
Text('Welcome!', style: TextStyle(fontSize: 24, color: Colors.blue)),
Text('Profile', style: TextStyle(fontSize: 24, color: Colors.blue)),
Text('Settings', style: TextStyle(fontSize: 18, color: Colors.grey)),
Text('About', style: TextStyle(fontSize: 18, color: Colors.grey)),
],
);
The Do: Define your styles once in your app's theme, then use them everywhere. It's like having a style guide that your whole app follows. Want to change all your headings from blue to green? Just change it in one place!
// Do: Define theme once
MaterialApp(
theme: ThemeData(
textTheme: TextTheme(
headlineMedium: TextStyle(fontSize: 24, color: Colors.blue),
bodyLarge: TextStyle(fontSize: 18, color: Colors.grey[600]),
),
),
);
// Use theme everywhere
Column(
children: [
Text('Welcome!', style: Theme.of(context).textTheme.headlineMedium),
Text('Profile', style: Theme.of(context).textTheme.headlineMedium),
Text('Settings', style: Theme.of(context).textTheme.bodyLarge),
Text('About', style: Theme.of(context).textTheme.bodyLarge),
],
);
6. Do: Navigate Like a Pro with Named Routes
Navigation is like the roads in your app – users need clear paths to get where they're going. Let's make sure they don't get lost!
The Don't: Don't use direct navigation with anonymous routes everywhere. It's like giving directions by saying "turn left, then right, then left again" instead of using street names.
// Don't: Anonymous routes everywhere
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ProfileScreen()),
);
// Later in another file...
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ProfileScreen()),
);
The Do: Use named routes or even better, a routing package like go_router. It's like having a GPS for your app!
Option 1: Named Routes (Built-in)
// Define routes once in your app
MaterialApp(
routes: {
'/': (context) => HomeScreen(),
'/profile': (context) => ProfileScreen(),
'/settings': (context) => SettingsScreen(),
},
);
// Navigate using names
Navigator.pushNamed(context, '/profile');
Option 2: go_router (Recommended for larger apps)
// Define your router
final router = GoRouter(
routes: [
GoRoute(path: '/', builder: (context, state) => HomeScreen()),
GoRoute(path: '/profile/:id', builder: (context, state) =>
ProfileScreen(userId: state.pathParameters['id']!)),
GoRoute(path: '/settings', builder: (context, state) => SettingsScreen()),
],
);
// Use it in your app
MaterialApp.router(routerConfig: router);
// Navigate with parameters
context.go('/profile/123');
context.push('/settings');
Why go_router is awesome:
- URL-based navigation (great for web apps)
- Easy parameter passing
- Deep linking support
- Navigation guards for authentication
// Pro tip: Protect routes with guards
GoRoute(
path: '/admin',
redirect: (context, state) {
final isLoggedIn = checkAuth(); // Auth check
return isLoggedIn ? null : '/login';
},
builder: (context, state) => AdminScreen(),
),
Wrapping Up
These simple "Dos" and "Don'ts" will help you write Flutter code that is:
- Clean: Easy to read and maintain
- Fast: Efficient and responsive
- Scalable: Works on all screen sizes
- Consistent: Looks professional and polished
Remember, great apps aren't just about what users see – they're also about the quality of the code behind the scenes.
Top comments (0)