> Frontend/Flutter
Flutter_14. Theme in Widget & Dark Mode & Named Constructor & For in
Janku
2023. 5. 12. 20:25
0) 학습 내용
- main.dart에서 작성된 Theme 을 widget에서 사용
- Dark Mode
- Constructor ( named Constructor)
- For in
1) main.dart에서 작성된 Theme 을 widget에서 사용
... 생략
theme: ThemeData().copyWith(
... 생략
- main.dart에서 만들어진 theme 을 widget에서 사용해보기 (expenses_list.dart에서)
return Dismissible(
key: ValueKey(expenses[index]), //create key obj
background: Container(color: Theme.of(context).colorScheme.error.withOpacity(0.3), // setting up background color
margin: EdgeInsets.symmetric(horizontal: Theme.of(context).cardTheme.margin!.horizontal),
),
child: ExpenseItem(expense: expenses[index]),
onDismissed: (direction) {
- main.dart에서 사용된 Theme of(context로 받아와), 해당 theme에 설정된 colorScheme 내에 error 색상으로 설정
- 동일하게, 설정된 cardTheme에서 설정된 margin값 가져와서 사용
cardTheme: const CardTheme().copyWith(
color: kColorScheme.secondaryContainer,
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8)),
- cardTheeme 내 margin의 horizontal은 16으로 설정되어 있음.
2) Dark Mode
import 'package:flutter/material.dart';
import 'package:flutter06_expense/widgets/expenses.dart';
// generates a color scheme based on a seed color
var kColorScheme = ColorScheme.fromSeed(
seedColor: const Color.fromARGB(255, 96, 59, 181),
);
var kDarkColorScheme = ColorScheme.fromSeed(
brightness: Brightness.dark,
// need to set Brightness => otherwise, bright will be white
seedColor: const Color.fromARGB(255, 5, 99, 125),
);
void main() {
runApp(
MaterialApp(
// copyWith: uses default useMaterial3 setting, no need to start from scratch
// the theme of the MaterialApp by copying the default theme using ThemeData().copyWith()
darkTheme: ThemeData.dark().copyWith(
useMaterial3: true,
colorScheme: kDarkColorScheme,
cardTheme: const CardTheme().copyWith(
color: kDarkColorScheme.secondaryContainer,
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8)),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: kDarkColorScheme.primaryContainer,
// primary container is the background color
foregroundColor: kDarkColorScheme
.onPrimaryContainer), //colors the content inside of that button: onPrimaryContainer
),
),
theme: ThemeData().copyWith(
useMaterial3: true,
// add more settings below
// 1. colorScheme
colorScheme: kColorScheme,
// 2. app-bar theme
appBarTheme: const AppBarTheme().copyWith(
backgroundColor: kColorScheme.onPrimaryContainer,
// this foregroundColor will overwrite other foreground theme
foregroundColor: kColorScheme.primaryContainer),
cardTheme: const CardTheme().copyWith(
color: kColorScheme.secondaryContainer,
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8)),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: kColorScheme.primaryContainer),
),
textTheme: ThemeData().textTheme.copyWith(
titleLarge: TextStyle(
fontWeight: FontWeight.w900,
color: kColorScheme.onSecondaryContainer,
fontSize: 20,
),
)),
themeMode: ThemeMode.light,
// change material 2 to material3
home: const Expenses(),
),
);
}
- main.dart.에 kDarkColorScheme을 설정해준다.
- 기존에 작성된 theme 위에 darkTheme 을 설정하고, 해당 theme에서 사용할 Theme을 설정한다.
- 이때, colorScheme: kDarkColorScheme으로 설정하여, fromSeed 를 통해 설정한 기본 color set으로 설정
themeMode: ThemeMode.light,
- 이후, 아래 쪽에 ThemeMode.system으로 설정하여, 기기의 설정에 따라 다르게 색상이 보여지도록 설정

- 위의 사진과 같이 dark mode 가 적용된다.
3) Constructor ( named Constructor)
- 인스턴스가 생성될 때, 호출되는 초기화 메소드
- 기본 생성자는 클래스의 이름과 같으며, 리턴값이 없고, 자동 생성된다.
- params를 전달해줄때는 따로 생성자를 생겅해야한다.
- Named Constructors를 사용하면, 하나의 클래스에 다양한 종류의 생성자를 생성할 수 있다.
- 클래스의 생성자 실행 순서로 초기화 목록 -> 상위 클래스의 기본 생성자 -> 하위 클래스의 기본 생성자.
- 초기화 목록(Initializer List)를 사용하면, 생성자 실행전에 변수 초기화 가능하고, 생성자 옆에 : 으로 시작하면 , 으로 구분
class ExpenseBucket {
const ExpenseBucket({required this.category, required this.expenses});
// adding my own alternative names constructor
// in order to filter out the expenses that belong to specific category
// need initializer
ExpenseBucket.forCategory(List<Expense> allExpenses, this.category)
: expenses = allExpenses.where((expense) => expense.category == category).toList();
final Category category;
final List<Expense> expenses;
// adding getter
double get totalExpenses {
double sum = 0;
for (final expense in expenses) {
sum += expense.amount;
}
return sum;
}
}
- expense.dart에서 ExpenseBucket을 생성
- 해당 class 에서, forCategory라는 named constructor를 사용했는데, 초기화 목록을 사용하여, 우선순위 최상위에 넣어준다.
- 이 constructor는 받은 allExpenses 라는 값을 iterate하면서, 받은 category가 해당 파일에 생성된 expense.category 와 동일한지 확인하고, 동일한 경우, 아래 생성된 expenses에 넣어준다.
4) Code 추가 및 for in
import 'package:flutter/material.dart';
import 'package:flutter06_expense/widgets/chart/chart_bar.dart';
import 'package:flutter06_expense/models/expense.dart';
class Chart extends StatelessWidget {
const Chart({super.key, required this.expenses});
final List<Expense> expenses;
List<ExpenseBucket> get buckets { // helper getters
return [
ExpenseBucket.forCategory(expenses, Category.food),
ExpenseBucket.forCategory(expenses, Category.leisure),
ExpenseBucket.forCategory(expenses, Category.travel),
ExpenseBucket.forCategory(expenses, Category.work),
];
}
double get maxTotalExpense {
double maxTotalExpense = 0;
for (final bucket in buckets) {
if (bucket.totalExpenses > maxTotalExpense) {
maxTotalExpense = bucket.totalExpenses;
}
}
return maxTotalExpense;
}
@override
Widget build(BuildContext context) {
final isDarkMode =
MediaQuery.of(context).platformBrightness == Brightness.dark;
return Container(
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.symmetric(
vertical: 16,
horizontal: 8,
),
width: double.infinity,
height: 180,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
gradient: LinearGradient(
colors: [
Theme.of(context).colorScheme.primary.withOpacity(0.3),
Theme.of(context).colorScheme.primary.withOpacity(0.0)
],
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
),
),
child: Column(
children: [
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
for (final bucket in buckets) // alternative to map()
ChartBar(
fill: bucket.totalExpenses == 0
? 0
: bucket.totalExpenses / maxTotalExpense,
)
],
),
),
const SizedBox(height: 12),
Row(
children: buckets
.map(
(bucket) => Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Icon(
categoryIcons[bucket.category],
color: isDarkMode
? Theme.of(context).colorScheme.secondary
: Theme.of(context)
.colorScheme
.primary
.withOpacity(0.7),
),
),
),
)
.toList(),
)
],
),
);
}
}
- chart.dart
import 'package:flutter/material.dart';
class ChartBar extends StatelessWidget {
const ChartBar({
super.key,
required this.fill,
});
final double fill;
@override
Widget build(BuildContext context) {
final isDarkMode =
// media query:: get environment information of which your app is running
MediaQuery.of(context).platformBrightness == Brightness.dark;
return Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: FractionallySizedBox(
heightFactor: fill,
child: DecoratedBox(
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius:
const BorderRadius.vertical(top: Radius.circular(8)),
color: isDarkMode
? Theme.of(context).colorScheme.secondary
: Theme.of(context).colorScheme.primary.withOpacity(0.65),
),
),
),
),
);
}
}
- chart_bar.dart
children: [
for (final bucket in buckets) // alternative to map()
ChartBar(
fill: bucket.totalExpenses == 0
? 0
: bucket.totalExpenses / maxTotalExpense,
)
],
- for final expense in expenses, : 받아온 expenses에서 하나씩 iterate해서 나온 값을 기준으로 ChartBar에 보내준다.
List<ExpenseBucket> get buckets { // helper getters
return [
ExpenseBucket.forCategory(expenses, Category.food),
ExpenseBucket.forCategory(expenses, Category.leisure),
ExpenseBucket.forCategory(expenses, Category.travel),
ExpenseBucket.forCategory(expenses, Category.work),
];
}
- ExpenseBucket 에서 만들어진 named constructor 인 forCategory에 expenses 와 Category. * 을 전달