Appearance
🧩 Components & Widgets
Overview
Standards for creating reusable UI components and widgets in mobile applications.
🧩 Components & Widgets Location
Shared Components
shared/components/: put the global reusable components here.- for related components, create a sub folder and put the components there.
Feature Widgets
features/feature_name/presentation/widgets/: put the widgets specific to the feature here.- for related components, create a sub folder and put the components there.
Component Design Principles
- Single Responsibility Principle
- Composition over inheritance
- Props/parameters validation
- Consistent naming conventions
Best Practices
- Create small, focused components
- Prefer StatelessWidget unless local state is required.
- Avoid deeply nested or rebuild-heavy widgets;extract parts into smaller widgets.
- Keep build methods short (<100 lines).
- Use const constructors and final fields whenever possible for performance.
- Support light/dark themes if applicable.
- Use named parameters for clarity.
- Avoid rebuilding the whole screen for small state changes
- Handle nulls gracefully; avoid ! operator.
- Avoid side effects inside UI code.
- Use the already defined custom components in the app insted of flutter's built-in components, like:
AppTextinsted ofText,AppButtoninsted ofElevatedButton, etc. - make sure that the component supports both LTR and RTL layouts.
- make sure that the component supports both dark and light themes.
Components Sub Folders
bottom_sheet/: Global usage bottom sheets should be added here.buttons/: If you add any new general usage button component (not specific to a feature), add it here, if any of the already existing buttons are enough for your needs, use them instead of creating a new one.date_and_time/: General usage date and time pickers or custom components should be added here.dialogs/: General usage dialogs should be added here.form_components/: General usage form components should be added here.
Examples
Good Example ✅
dart
// Good: Focused widget
class Button extends StatelessWidget {
final Widget? child;
final VoidCallback? onPressed;
final Color? color;
const Button({
Key? key,
this.child,
this.onPressed,
this.color,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(backgroundColor: color),
child: child,
);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Bad Example ❌
dart
// Bad: Overly complex widget with too many responsibilities
class ComplexWidget extends StatelessWidget {
final bool isLoading;
final List<String> data;
final String errorMessage;
const ComplexWidget({
Key? key,
required this.isLoading,
required this.data,
required this.errorMessage,
}) : super(key: key);
@override
Widget build(BuildContext context) {
if (isLoading) {
return const CircularProgressIndicator();
} else if (errorMessage.isNotEmpty) {
return Text('Error: $errorMessage');
} else {
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return ListTile(title: Text(data[index]));
},
);
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29