Di chuyển từ Material 2 sang Material 3 trong Compose

Material Design 3 là phiên bản cải tiến tiếp theo của Material Design. Phiên bản này có giao diện cập nhật, cũng như các thành phần và tính năng cá nhân hoá Material You, chẳng hạn như màu động. Đây là bản cập nhật cho Material Design 2 và phù hợp với phong cách đồ hoạ cũng như giao diện người dùng hệ thống mới trên Android 12 trở lên.

Hướng dẫn này tập trung vào việc di chuyển từ Thư viện Jetpack Compose Material (androidx.compose.material) sang Thư viện Jetpack Compose Material 3 (androidx.compose.material3).

Phương pháp

Nói chung, về lâu dài, bạn không nên sử dụng cả M2 và M3 trong một ứng dụng. Điều này là do 2 hệ thống thiết kế và thư viện tương ứng có khác biệt đáng kể về mặt thiết kế trải nghiệm người dùng/giao diện người dùng và cách triển khai Compose.

Ứng dụng của bạn có thể sử dụng một hệ thống thiết kế, chẳng hạn như hệ thống được tạo bằng Figma. Trong những trường hợp như vậy, bạn hoặc nhóm thiết kế của bạn nên chuyển từ M2 sang M3 trước khi bắt đầu di chuyển Compose. Sẽ không hợp lý nếu bạn di chuyển ứng dụng sang M3 nếu thiết kế trải nghiệm người dùng/giao diện người dùng của ứng dụng đó dựa trên M2.

Hơn nữa, phương pháp di chuyển của bạn sẽ thay đổi tuỳ thuộc vào kích thước, độ phức tạp và thiết kế của trải nghiệm người dùng/giao diện người dùng của ứng dụng. Điều này sẽ giúp bạn giảm thiểu tác động đến cơ sở mã của mình. Bạn nên áp dụng phương pháp di chuyển theo từng giai đoạn.

Thời điểm di chuyển

Bạn nên bắt đầu di chuyển càng sớm càng tốt. Tuy nhiên, bạn cần cân nhắc xem ứng dụng của mình có đang ở tình trạng lý tưởng để di chuyển hoàn toàn từ M2 sang M3 hay không. Có một số trường hợp chặn cần xem xét kiểm tra trước khi bạn bắt đầu:

Trường hợp Phương pháp đề xuất
Không có trình chặn Bắt đầu di chuyển theo giai đoạn
Một thành phần trong M2 chưa có trong M3. Hãy xem mục Thành phần và bố cục bên dưới. Bắt đầu di chuyển theo giai đoạn
Bạn hoặc nhóm thiết kế của bạn chưa di chuyển hệ thống thiết kế của ứng dụng từ M2 sang M3 Hãy di chuyển hệ thống thiết kế từ M2 sang M3, sau đó bắt đầu di chuyển theo giai đoạn

Ngay cả khi bị ảnh hưởng bởi các trường hợp trên, bạn cũng nên áp dụng phương pháp di chuyển theo giai đoạn trước khi cam kết và phát hành một bản cập nhật ứng dụng. Trong những trường hợp này, bạn sẽ sử dụng song song M2 và M3, rồi dần dần loại bỏ M2 trong khi chuyển sang M3.

Phương pháp theo giai đoạn

Sau đây là các bước chung để di chuyển theo giai đoạn:

  1. Thêm phần phụ thuộc M3 cùng với phần phụ thuộc M2.
  2. Thêm(các) phiên bản M3 của(các) giao diện ứng dụng cùng với(các) phiên bản M2 của(các) giao diện ứng dụng.
  3. Di chuyển từng mô-đun, màn hình hoặc thành phần kết hợp sang M3, tuỳ thuộc vào kích thước và độ phức tạp của ứng dụng (xem phần bên dưới để biết chi tiết).
  4. Sau khi di chuyển toàn bộ, hãy xoá(các) phiên bản M2 của(các) giao diện ứng dụng.
  5. Xoá phần phụ thuộc M2.

Phần phụ thuộc

M3 có gói và phiên bản riêng so với M2:

M2

implementation "androidx.compose.material:material:$m2-version"

M3

implementation "androidx.compose.material3:material3:$m3-version"

Xem các phiên bản M3 mới nhất trên trang phát hành Compose Material 3.

Các phần phụ thuộc Material khác bên ngoài thư viện M2 và M3 chính không thay đổi. Chúng sử dụng kết hợp các gói M2 và M3 nhưng điều này không ảnh hưởng đến việc di chuyển. Bạn có thể sử dụng nguyên trạng các phần phụ thuộc này với M3:

Thư viện Gói và phiên bản
Biểu tượng Compose Material androidx.compose.material:material-icons-*:$m2-version
Tính năng gợn sóng của Compose Material androidx.compose.material:material-ripple:$m2-version

API thử nghiệm

Một số API của M3 được coi là thử nghiệm. Trong những trường hợp như vậy, bạn cần chọn sử dụng ở cấp độ hàm hoặc tệp bằng cách sử dụng chú thích ExperimentalMaterial3Api:

import androidx.compose.material3.ExperimentalMaterial3Api

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AppComposable() {
    // M3 composables
}

Giao diện

Trong cả M2 và M3, thành phần kết hợp giao diện có tên là MaterialTheme nhưng các gói và tham số nhập lại khác nhau:

M2

import androidx.compose.material.MaterialTheme

MaterialTheme(
    colors = AppColors,
    typography = AppTypography,
    shapes = AppShapes
) {
    // M2 content
}

M3

import androidx.compose.material3.MaterialTheme

MaterialTheme(
    colorScheme = AppColorScheme,
    typography = AppTypography,
    shapes = AppShapes
) {
    // M3 content
}

Màu

So sánh hệ thống màu M2 và M3
Hình 1. Hệ thống màu M2 (bên trái) so với hệ thống màu M3 (bên phải).

Hệ thống màu trong M3 khác biệt đáng kể so với M2. Số lượng tham số màu đã tăng lên, chúng có tên khác nhau và ánh xạ đến các thành phần M3 theo cách khác nhau. Trong Compose, điều này áp dụng cho lớp Colors của M2, lớp ColorScheme của M3 và các hàm liên quan:

M2

import androidx.compose.material.lightColors
import androidx.compose.material.darkColors

val AppLightColors = lightColors(
    // M2 light Color parameters
)
val AppDarkColors = darkColors(
    // M2 dark Color parameters
)
val AppColors = if (darkTheme) {
    AppDarkColors
} else {
    AppLightColors
}

M3

import androidx.compose.material3.lightColorScheme
import androidx.compose.material3.darkColorScheme

val AppLightColorScheme = lightColorScheme(
    // M3 light Color parameters
)
val AppDarkColorScheme = darkColorScheme(
    // M3 dark Color parameters
)
val AppColorScheme = if (darkTheme) {
    AppDarkColorScheme
} else {
    AppLightColorScheme
}

Do sự khác biệt đáng kể giữa hệ thống màu M2 và M3, không có cách ánh xạ hợp lý nào cho tham số Color. Thay vào đó, hãy sử dụng Công cụ tạo giao diện Material để tạo bảng phối màu M3. Hãy sử dụng màu M2 làm màu nguồn cốt lõi trong công cụ. Công cụ này sẽ mở rộng thành bảng sắc độ dùng trong bảng phối màu M3. Bạn nên bắt đầu từ các ánh xạ sau:

M2 Công cụ tạo giao diện Material
primary Chính
primaryVariant Secondary
secondary Thứ ba
surface hoặc background Trung tính
Màu M2 được dùng trong Công cụ tạo giao diện Material để tạo bảng phối màu M3
Hình 2. Màu M2 của Jetchat được dùng trong Công cụ tạo giao diện Material để tạo bảng phối màu M3.

Bạn có thể sao chép các giá trị mã màu hex cho các giao diện sáng và tối từ công cụ này và sử dụng các giá trị đó để triển khai thực thể M3 ColorScheme. Ngoài ra, Công cụ tạo giao diện Material có thể xuất mã Compose.

isLight

Không giống như lớp M2 Colors, lớp M3 ColorScheme không có tham số isLight. Nhìn chung, bạn nên thử và lập mô hình bất kỳ thông tin nào cần ở cấp độ giao diện này. Ví dụ:

M2

import androidx.compose.material.lightColors
import androidx.compose.material.darkColors
import androidx.compose.material.MaterialTheme

@Composable
private fun AppTheme(
  darkTheme: Boolean = isSystemInDarkTheme(),
  content: @Composable () -> Unit
) {
  val colors = if (darkTheme) darkColors(…) else lightColors(…)
  MaterialTheme(
      colors = colors,
      content = content
  )
}

@Composable
fun AppComposable() {
    AppTheme {
        val cardElevation = if (MaterialTheme.colors.isLight) 0.dp else 4.dp
        …
    }
}

M3

import androidx.compose.material3.lightColorScheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.MaterialTheme

val LocalCardElevation = staticCompositionLocalOf { Dp.Unspecified }
@Composable
private fun AppTheme(
   darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
   val cardElevation = if (darkTheme) 4.dp else 0.dp
    CompositionLocalProvider(LocalCardElevation provides cardElevation) {
        val colorScheme = if (darkTheme) darkColorScheme(…) else lightColorScheme(…)
        MaterialTheme(
            colorScheme = colorScheme,
            content = content
        )
    }
}

@Composable
fun AppComposable() {
    AppTheme {
        val cardElevation = LocalCardElevation.current
        …
    }
}

Hãy xem hướng dẫn Hệ thống thiết kế tuỳ chỉnh trong Compose để biết thêm thông tin.

Màu động

Một tính năng mới trong M3 là màu động. Thay vì sử dụng màu tuỳ chỉnh, M3 ColorScheme có thể tận dụng màu hình nền của thiết bị trên Android 12 trở lên bằng các hàm sau:

Kiểu chữ

So sánh hệ thống kiểu chữ M2 và M3
Hình 3. Hệ thống kiểu chữ M3 (bên trái) so với hệ thống kiểu chữ M2 (bên phải)

Hệ thống kiểu chữ trong M3 khác với M2. Số lượng tham số kiểu chữ gần giống nhau, nhưng chúng có tên khác nhau và ánh xạ đến các thành phần M3 theo cách khác nhau. Trong Compose, điều này áp dụng cho lớp M2 Typography và lớp M3 Typography:

M2

import androidx.compose.material.Typography

val AppTypography = Typography(
    // M2 TextStyle parameters
)

M3

import androidx.compose.material3.Typography

val AppTypography = Typography(
    // M3 TextStyle parameters
)

Bạn nên bắt đầu từ các ánh xạ tham số TextStyle sau:

M2 M3
h1 displayLarge
h2 displayMedium
h3 displaySmall
Không có headlineLarge
h4 headlineMedium
h5 headlineSmall
h6 titleLarge
subtitle1 titleMedium
subtitle2 titleSmall
body1 bodyLarge
body2 bodyMedium
caption bodySmall
button labelLarge
Không áp dụng labelMedium
overline labelSmall

Hình dạng

So sánh hệ thống hình dạng M2 và M3
Hình 4. Hệ thống hình dạng M2 (bên trái) so với hệ thống hình dạng M3 (bên phải)

Hệ thống hình dạng trong M3 khác với M2. Số lượng tham số hình dạng đã tăng lên, các tên gọi khác nhau và ánh xạ đến các thành phần M3 theo cách khác nhau. Trong Compose, điều này áp dụng cho lớp M2 Shapes và lớp M3 Shapes:

M2

import androidx.compose.material.Shapes

val AppShapes = Shapes(
    // M2 Shape parameters
)

M3

import androidx.compose.material3.Shapes

val AppShapes = Shapes(
    // M3 Shape parameters
)

Bạn nên bắt đầu từ các ánh xạ tham số Shape sau:

M2 M3
Không có extraSmall
small small
medium medium
large large
Không áp dụng extraLarge

Thành phần và bố cục

Hầu hết các thành phần và bố cục trong M2 đều có trong M3. Tuy nhiên, có một số tệp bị thiếu, cũng như các tệp mới không tồn tại trong M2. Hơn nữa, một số thành phần M3 có nhiều biến thể hơn các phiên bản tương đương trong M2. Nhìn chung, các nền tảng API của M3 cố gắng giống nhất có thể với các nền tảng tương đương gần nhất trong M2.

Do các hệ thống màu, kiểu chữ và hình dạng được cập nhật, các thành phần M3 có xu hướng ánh xạ khác nhau đến các giá trị giao diện mới. Bạn nên xem thư mục mã thông báo trong mã nguồn Compose Material 3 làm nguồn chính xác cho các ánh xạ này.

Mặc dù một số thành phần cần được xem xét đặc biệt, nhưng bạn nên bắt đầu từ các ánh xạ hàm sau đây:

Các API bị thiếu:

M2 M3
androidx.compose.material.swipeable Chưa có

Các API đã thay thế:

M2 M3
androidx.compose.material.BackdropScaffold Không có phiên bản M3 tương đương, hãy di chuyển sang Scaffold hoặc BottomSheetScaffold
androidx.compose.material.BottomDrawer Không có phiên bản M3 tương đương, hãy di chuyển sang ModalBottomSheet

Các API đã đổi tên:

M2 M3
androidx.compose.material.BottomNavigation androidx.compose.material3.NavigationBar
androidx.compose.material.BottomNavigationItem androidx.compose.material3.NavigationBarItem
androidx.compose.material.Chip androidx.compose.material3.AssistChip hoặc androidx.compose.material3.SuggestionChip
androidx.compose.material.ModalBottomSheetLayout androidx.compose.material3.ModalBottomSheet
androidx.compose.material.ModalDrawer androidx.compose.material3.ModalNavigationDrawer

Tất cả các API khác:

M2 M3
androidx.compose.material.AlertDialog androidx.compose.material3.AlertDialog
androidx.compose.material.Badge androidx.compose.material3.Badge
androidx.compose.material.BadgedBox androidx.compose.material3.BadgedBox
androidx.compose.material.BottomAppBar androidx.compose.material3.BottomAppBar
androidx.compose.material.BottomSheetScaffold androidx.compose.material3.BottomSheetScaffold
androidx.compose.material.Button androidx.compose.material3.Button
androidx.compose.material.Card androidx.compose.material3.Card
androidx.compose.material.Checkbox androidx.compose.material3.Checkbox
androidx.compose.material.CircularProgressIndicator androidx.compose.material3.CircularProgressIndicator
androidx.compose.material.Divider androidx.compose.material3.Divider
androidx.compose.material.DropdownMenu androidx.compose.material3.DropdownMenu
androidx.compose.material.DropdownMenuItem androidx.compose.material3.DropdownMenuItem
androidx.compose.material.ExposedDropdownMenuBox androidx.compose.material3.ExposedDropdownMenuBox
androidx.compose.material.ExtendedFloatingActionButton androidx.compose.material3.ExtendedFloatingActionButton
androidx.compose.material.FilterChip androidx.compose.material3.FilterChip
androidx.compose.material.FloatingActionButton androidx.compose.material3.FloatingActionButton
androidx.compose.material.Icon androidx.compose.material3.Icon
androidx.compose.material.IconButton androidx.compose.material3.IconButton
androidx.compose.material.IconToggleButton androidx.compose.material3.IconToggleButton
androidx.compose.material.LeadingIconTab androidx.compose.material3.LeadingIconTab
androidx.compose.material.LinearProgressIndicator androidx.compose.material3.LinearProgressIndicator
androidx.compose.material.ListItem androidx.compose.material3.ListItem
androidx.compose.material.NavigationRail androidx.compose.material3.NavigationRail
androidx.compose.material.NavigationRailItem androidx.compose.material3.NavigationRailItem
androidx.compose.material.OutlinedButton androidx.compose.material3.OutlinedButton
androidx.compose.material.OutlinedTextField androidx.compose.material3.OutlinedTextField
androidx.compose.material.RadioButton androidx.compose.material3.RadioButton
androidx.compose.material.RangeSlider androidx.compose.material3.RangeSlider
androidx.compose.material.Scaffold androidx.compose.material3.Scaffold
androidx.compose.material.ScrollableTabRow androidx.compose.material3.ScrollableTabRow
androidx.compose.material.Slider androidx.compose.material3.Slider
androidx.compose.material.Snackbar androidx.compose.material3.Snackbar
androidx.compose.material.Switch androidx.compose.material3.Switch
androidx.compose.material.Tab androidx.compose.material3.Tab
androidx.compose.material.TabRow androidx.compose.material3.TabRow
androidx.compose.material.Text androidx.compose.material3.Text
androidx.compose.material.TextButton androidx.compose.material3.TextButton
androidx.compose.material.TextField androidx.compose.material3.TextField
androidx.compose.material.TopAppBar androidx.compose.material3.TopAppBar
androidx.compose.material.TriStateCheckbox androidx.compose.material3.TriStateCheckbox

Xem các thành phần và bố cục M3 mới nhất trên trang tổng quan tài liệu tham khảo về API Compose Material 3, đồng thời theo dõi trang phát hành để biết các API mới và API cập nhật.

Scaffold, thanh thông báo nhanh và ngăn điều hướng

So sánh scaffold M2 và M3 với thanh thông báo nhanh và ngăn điều hướng
Hình 5. Scaffold M2 có thanh thông báo nhanh và ngăn điều hướng (bên trái) so với scaffold M3 có thanh thông báo nhanh và ngăn điều hướng (bên phải).

Scaffold trong M3 khác với M2. Trong cả M2 và M3, thành phần kết hợp bố cục chính được đặt tên là Scaffold nhưng các gói nhập và tham số khác nhau:

M2

import androidx.compose.material.Scaffold

Scaffold(
    // M2 scaffold parameters
)

M3

import androidx.compose.material3.Scaffold

Scaffold(
    // M3 scaffold parameters
)

M2 Scaffold chứa tham số backgroundColor hiện được đặt tên là containerColor trong M3 Scaffold:

M2

import androidx.compose.material.Scaffold

Scaffold(
    backgroundColor = …,
    content = { … }
)

M3

import androidx.compose.material3.Scaffold

Scaffold(
    containerColor = …,
    content = { … }
)

Lớp M2 ScaffoldState không còn tồn tại trong M3 vì lớp này chứa tham số drawerState không còn cần thiết nữa. Để hiển thị thanh thông báo nhanh sử dụng M3 Scaffold, hãy sử dụng SnackbarHostState:

M2

import androidx.compose.material.Scaffold
import androidx.compose.material.rememberScaffoldState

val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()

Scaffold(
    scaffoldState = scaffoldState,
    content = {
        …
        scope.launch {
            scaffoldState.snackbarHostState.showSnackbar(…)
        }
    }
)

M3

import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState

val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()

Scaffold(
    snackbarHost = { SnackbarHost(snackbarHostState) },
    content = {
        …
        scope.launch {
            snackbarHostState.showSnackbar(…)
        }
    }
)

Tất cả tham số drawer* từ Scaffold của M2 đã bị xoá khỏi Scaffold của M3. Các tham số này bao gồm cả những tham số như drawerShapedrawerContent. Để hiển thị một ngăn bằng Scaffold M3, hãy sử dụng thành phần kết hợp ngăn điều hướng, chẳng hạn như ModalNavigationDrawer:

M2

import androidx.compose.material.DrawerValue
import
import androidx.compose.material.Scaffold
import androidx.compose.material.rememberDrawerState
import androidx.compose.material.rememberScaffoldState

val scaffoldState = rememberScaffoldState(
    drawerState = rememberDrawerState(DrawerValue.Closed)
)
val scope = rememberCoroutineScope()

Scaffold(
    scaffoldState = scaffoldState,
    drawerContent = { … },
    drawerGesturesEnabled = …,
    drawerShape = …,
    drawerElevation = …,
    drawerBackgroundColor = …,
    drawerContentColor = …,
    drawerScrimColor = …,
    content = {
        …
        scope.launch {
            scaffoldState.drawerState.open()
        }
    }
)

M3

import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ModalDrawerSheet
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.Scaffold
import androidx.compose.material3.rememberDrawerState

val drawerState = rememberDrawerState(DrawerValue.Closed)
val scope = rememberCoroutineScope()

ModalNavigationDrawer(
    drawerState = drawerState,
    drawerContent = {
        ModalDrawerSheet(
            drawerShape = …,
            drawerTonalElevation = …,
            drawerContainerColor = …,
            drawerContentColor = …,
            content = { … }
        )
    },
    gesturesEnabled = …,
    scrimColor = …,
    content = {
        Scaffold(
            content = {
                …
                scope.launch {
                    drawerState.open()
                }
            }
        )
    }
)

Thanh ứng dụng trên cùng

So sánh scaffold M2 và M3 với thanh ứng dụng trên cùng và danh sách cuộn
Hình 6. Scaffold M2 có thanh ứng dụng trên cùng và danh sách cuộn (bên trái) so với Scaffold M3 có thanh ứng dụng trên cùng và danh sách cuộn (bên phải)

Thanh ứng dụng hàng đầu trong M3 khác với các thanh trong M2. Trong cả M2 và M3, thành phần kết hợp thanh ứng dụng hàng đầu chính có tên là TopAppBar nhưng gói nhập và tham số sẽ khác nhau:

M2

import androidx.compose.material.TopAppBar

TopAppBar(…)

M3

import androidx.compose.material3.TopAppBar

TopAppBar(…)

Hãy cân nhắc sử dụng M3 CenterAlignedTopAppBar nếu trước đó bạn đã căn giữa nội dung trong M2 TopAppBar. Bạn cũng nên biết về MediumTopAppBarLargeTopAppBar.

Các thanh ứng dụng trên cùng của M3 chứa tham số scrollBehavior mới để cung cấp chức năng khác khi cuộn qua lớp TopAppBarScrollBehavior, chẳng hạn như thay đổi độ cao. Tính năng này hoạt động cùng với nội dung cuộn qua Modifer.nestedScroll. Bạn có thể thực hiện việc này trong M2 TopAppBar bằng cách thay đổi tham số elevation theo cách thủ công:

M2

import androidx.compose.material.AppBarDefaults
import androidx.compose.material.Scaffold
import androidx.compose.material.TopAppBar

val state = rememberLazyListState()
val isAtTop by remember {
    derivedStateOf {
        state.firstVisibleItemIndex == 0 && state.firstVisibleItemScrollOffset == 0
    }
}

Scaffold(
    topBar = {
        TopAppBar(
            elevation = if (isAtTop) {
                0.dp
            } else {
                AppBarDefaults.TopAppBarElevation
            },
            …
        )
    },
    content = {
        LazyColumn(state = state) { … }
    }
)

M3

import androidx.compose.material3.Scaffold
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults

val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()

Scaffold(
    modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
    topBar = {
        TopAppBar(
            scrollBehavior = scrollBehavior,
            …
        )
    },
    content = {
        LazyColumn { … }
    }
)

Thanh điều hướng dưới cùng/Thanh điều hướng

So sánh thanh điều hướng dưới cùng M2 và thanh điều hướng M3
Hình 7. Thanh điều hướng dưới cùng M2 (bên trái) so với thanh điều hướng M3 (bên phải).

Thanh điều hướng dưới cùng trong M2 đã được đổi tên thành thanh điều hướng trong M3. Trong M2, có các thành phần kết hợp BottomNavigationBottomNavigationItem, còn trong M3, có các thành phần kết hợp NavigationBarNavigationBarItem:

M2

import androidx.compose.material.BottomNavigation
import androidx.compose.material.BottomNavigationItem

BottomNavigation {
    BottomNavigationItem(…)
    BottomNavigationItem(…)
    BottomNavigationItem(…)
}

M3

import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem

NavigationBar {
    NavigationBarItem(…)
    NavigationBarItem(…)
    NavigationBarItem(…)
}

Nút, nút biểu tượng và FAB

So sánh nút M2 và M3
Hình 8. Nút M2 (bên trái) so với nút M3 (bên phải)

Các nút, nút biểu tượng và nút hành động nổi (FAB) trong M3 khác với các nút trong M2. M3 bao gồm tất cả thành phần kết hợp nút của M2:

M2

import androidx.compose.material.Button
import androidx.compose.material.ExtendedFloatingActionButton
import androidx.compose.material.FloatingActionButton
import androidx.compose.material.IconButton
import androidx.compose.material.IconToggleButton
import androidx.compose.material.OutlinedButton
import androidx.compose.material.TextButton

// M2 buttons
Button(…)
OutlinedButton(…)
TextButton(…)
// M2 icon buttons
IconButton(…)
IconToggleButton(…)
// M2 FABs
FloatingActionButton(…)
ExtendedFloatingActionButton(…)

M3

import androidx.compose.material3.Button
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconToggleButton
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.TextButton

// M3 buttons
Button(…)
OutlinedButton(…)
TextButton(…)
// M3 icon buttons
IconButton(…)
IconToggleButton(…)
// M3 FABs
FloatingActionButton(…)
ExtendedFloatingActionButton(…)

M3 cũng bao gồm các biến thể nút mới. Hãy xem các nút mới này trên trang Tổng quan về tài liệu tham khảo API Compose Material 3.

Chuyển

So sánh nút chuyển M2 và M3
Hình 9. Nút chuyển M2 (bên trái) so với nút chuyển M3 (bên phải).

Nút chuyển trong M3 khác với M2. Trong cả M2 và M3, thành phần kết hợp nút chuyển được đặt tên là Switch, nhưng gói nhập khác nhau:

M2

import androidx.compose.material.Switch

Switch(…)

M3

import androidx.compose.material3.Switch

Switch(…)

Bề mặt và độ nâng

So sánh độ nâng bề mặt M2 và độ nâng bề mặt M3 trong giao diện sáng và tối
Hình 10. Độ nâng bề mặt M2 so với độ nâng bề mặt M3 trong giao diện sáng (bên trái) và giao diện tối (bên phải).

Hệ thống bề mặt và độ nâng trong M3 khác với M2. Có 2 loại độ nâng trong M3:

  • Độ cao đổ bóng (truyền một bóng, giống như M2)
  • Độ cao sắc độ (phủ lên màu, mới có trong M3)

Trong Compose, loại này áp dụng cho hàm M2 Surface và hàm M3 Surface:

M2

import androidx.compose.material.Surface

Surface(
    elevation = …
) { … }

M3

import androidx.compose.material3.Surface

Surface(
    shadowElevation = …,
    tonalElevation = …
) { … }

Bạn có thể sử dụng các giá trị elevation Dp trong M2 cho shadowElevation và/hoặc tonalElevation trong M3, tuỳ thuộc vào lựa chọn ưu tiên về thiết kế trải nghiệm người dùng/giao diện người dùng. Surface là thành phần kết hợp hỗ trợ đằng sau hầu hết các thành phần, vì vậy, các thành phần kết hợp cũng có thể hiển thị các tham số độ cao mà bạn phải di chuyển theo cùng một cách.

Độ cao sắc độ trong M3 thay thế khái niệm lớp phủ độ cao trong giao diện tối M2. Do đó, ElevationOverlayLocalElevationOverlay không tồn tại trong M3 và LocalAbsoluteElevation trong M2 đã thay đổi thành LocalAbsoluteTonalElevation trong M3.

Điểm nhấn và alpha nội dung

So sánh biểu tượng và điểm nhấn văn bản của M2 và M3
Hình 11. Điểm nhấn văn bản và biểu tượng M2 (bên trái) so với điểm nhấn văn bản và biểu tượng M3 (bên phải)

Điểm nhấn trong M3 khác biệt đáng kể so với M2. Trong M2, điểm nhấn sử dụng màu nằm trên với các giá trị alpha nhất định để phân biệt nội dung như văn bản và biểu tượng. Có một vài phương pháp trong M3:

  • Sử dụng màu nằm trên cùng với màu biến thể nằm trên trên hệ thống màu M3 mở rộng.
  • Sử dụng nhiều độ đậm phông chữ cho văn bản.

Do đó, ContentAlphaLocalContentAlpha không tồn tại trong M3 và cần được thay thế.

Bạn nên bắt đầu từ các ánh xạ sau:

M2 M3
onSurface với ContentAlpha.high onSurface nói chung, FontWeight.MediumFontWeight.Black cho văn bản
onSurface với ContentAlpha.medium onSurfaceVariant nói chung, FontWeight.ThinFontWeight.Normal cho văn bản
onSurface với ContentAlpha.disabled onSurface.copy(alpha = 0.38f)

Dưới đây là ví dụ về điểm nhấn biểu tượng trong M2 so với M3:

M2

import androidx.compose.material.ContentAlpha
import androidx.compose.material.LocalContentAlpha

// High emphasis
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) {
    Icon(…)
}
// Medium emphasis
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
    Icon(…)
}
// Disabled emphasis
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) {
    Icon(…)
}

M3

import androidx.compose.material3.LocalContentColor

// High emphasis
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface) {
    Icon(…)
}
// Medium emphasis
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant) {
    Icon(…)
}
// Disabled emphasis
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)) {
    Icon(…)
}

Dưới đây là ví dụ về điểm nhấn văn bản trong M2 và M3:

M2

import androidx.compose.material.ContentAlpha
import androidx.compose.material.LocalContentAlpha

// High emphasis
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) {
    Text(…)
}
// Medium emphasis
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
    Text(…)
}
// Disabled emphasis
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) {
    Text(…)
}

M3

import androidx.compose.material3.LocalContentColor

// High emphasis
Text(
    …,
    fontWeight = FontWeight.Bold
)
// Medium emphasis
Text(
    …,
    fontWeight = FontWeight.Normal
)
// Disabled emphasis
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)) {
    Text(
        …,
        fontWeight = FontWeight.Normal
    )
}

Nền và vùng chứa

Nền trong M2 được đặt tên là vùng chứa trong M3. Nhìn chung, bạn có thể thay thế các tham số background* trong M2 bằng container* trong M3, sử dụng các giá trị giống nhau. Ví dụ:

M2

Badge(
    backgroundColor = MaterialTheme.colors.primary
) { … }

M3

Badge(
    containerColor = MaterialTheme.colorScheme.primary
) { … }

Để tìm hiểu thêm về việc di chuyển từ M2 sang M3 trong Compose, hãy tham khảo các tài nguyên khác sau đây.

Tài liệu

Ứng dụng mẫu

Video

Tài liệu tham khảo API và mã nguồn