1. Giriş
Materyal Tasarım, cesur ve güzel dijital ürünler oluşturmaya yönelik bir sistemdir. Ürün ekipleri, tarzı, markayı, etkileşimi ve hareketi tutarlı bir ilke ve bileşen grubu altında birleştirerek en yüksek tasarım potansiyellerini gerçekleştirebilir.
Material Bileşenleri (MDC), geliştiricilerin Material Design'u uygulamalarına yardımcı olur. Google'daki bir mühendis ve kullanıcı deneyimi tasarımcısı ekibi tarafından oluşturulan MDC, onlarca güzel ve işlevsel kullanıcı arayüzü bileşenine sahiptir ve Android, iOS, web ve Flutter'da kullanılabilir.material.io/develop |
Material'ın Flutter için hareket sistemi nedir?
Flutter için Materyal hareket sistemi, Materyal Tasarım yönergelerinde açıklandığı gibi kullanıcıların bir uygulamayı anlamasına ve uygulamada gezinmesine yardımcı olabilen, animasyon paketinde yer alan bir dizi geçiş kalıbıdır.
Dört temel Materyal geçiş kalıbı şunlardır:
- Kapsayıcı Dönüşümü: Kapsayıcı içeren kullanıcı arayüzü öğeleri arasında geçiş yapar. Bir öğeyi sorunsuz bir şekilde diğerine dönüştürerek iki farklı kullanıcı arayüzü öğesi arasında görünür bir bağlantı oluşturur.
- Paylaşılan Eksen: Üç boyutlu veya gezinme ilişkisi olan kullanıcı arayüzü öğeleri arasındaki geçişler; öğeler arasındaki ilişkiyi güçlendirmek için x, y veya z ekseninde ortak bir dönüşüm kullanır.
- Şeffaflaşarak geçiş: Birbiriyle güçlü bir ilişkisi olmayan kullanıcı arayüzü öğeleri arasındaki geçişler için kullanılır. Gelen öğenin ölçeğiyle birlikte sıralı bir şekilde karartma ve açma işlemi gerçekleştirir.
- Şeffaflaşma: Ekran sınırları içine giren veya dışına çıkan kullanıcı arayüzü öğeleri için kullanılır.
Animasyon paketi, bu kalıplar için hem Flutter animasyon kitaplığının (flutter/animation.dart
) hem de Flutter materyali kitaplığının (flutter/material.dart
) üzerine kurulan geçiş widget'ları sunar:
Bu codelab'de Flutter çerçevesi ve Material kitaplığı temel alınarak oluşturulan Material geçişlerini kullanacaksınız. Bu sayede widget'larla ilgileneceksiniz. :)
Oluşturacaklarınız
Bu codelab, uygulamanızın görünümünü ve tarzını özelleştirmek için animasyonlar paketindeki geçişleri nasıl kullanabileceğinizi göstermek amacıyla Dart'ı kullanarak Yanıtla adlı örnek bir Flutter e-posta uygulamasına bazı geçişler eklemeniz konusunda size yol gösterecektir.
Yanıtla uygulaması için başlangıç kodu sağlanacak. Ardından, uygulamaya aşağıdaki Material geçişlerini dahil edeceksiniz. Bunları, tamamlanan codelab'in GIF'inde görebilirsiniz:
- Kapsayıcı Dönüşümü'nde e-posta listesinden e-posta ayrıntıları sayfasına geçiş
- Kapsayıcı Dönüşümü'nde FAB'den e-posta oluşturma sayfasına geçiş
- Arama simgesinden arama görünümü sayfasına paylaşılan z ekseni geçişi
- Posta kutusu sayfaları arasında Şeffaflaşma geçişi
- Oluştur ve yanıtla FAB'leri arasında şeffaflaşma geçişi
- Kaybolan posta kutusu başlığı arasında Şeffaflaşma geçişi
- Alt uygulama çubuğu işlemleri arasında Kararma geçişi
Gerekenler
- Flutter geliştirme ve Dart ile ilgili temel bilgiler
- Kod düzenleyici
- Bir Android/iOS emülatörü veya cihazı
- Örnek kod (sonraki adıma bakın)
Flutter uygulamaları geliştirme deneyiminizi nasıl değerlendirirsiniz?
Bu codelab'den ne öğrenmek istersiniz?
2. Flutter geliştirme ortamınızı kurma
Bu laboratuvarı tamamlamak için Flutter SDK ve bir düzenleyici yazılımına ihtiyacınız vardır.
Aşağıdaki cihazlardan herhangi birini kullanarak kod laboratuvarını çalıştırabilirsiniz:
- Bilgisayarınıza bağlı ve Geliştirici moduna ayarlanmış fiziksel bir Android veya iOS cihaz.
- iOS simülasyon aracı (Xcode araçlarının yüklenmesi gerekir).
- Android Emülatör (Android Studio'da kurulum gerektirir).
- Tarayıcı (Hata ayıklama için Chrome gerekir).
- Windows, Linux veya macOS masaüstü uygulaması olarak Uygulamayı dağıtmayı planladığınız platformda gerçekleştirmeniz gerekir. Bu nedenle, bir Windows masaüstü uygulaması geliştirmek istiyorsanız uygun derleme zincirine erişmek için Windows'da geliştirme yapmanız gerekir. docs.flutter.dev/desktop adresinde işletim sistemine özgü gereksinimler ayrıntılı olarak açıklanmıştır.
3. codelab başlangıç uygulamasını indirme
1. Seçenek: Başlangıç kod laboratuvarının uygulamasını GitHub'dan kopyalama
Bu codelab'i GitHub'dan kopyalamak için aşağıdaki komutları çalıştırın:
git clone https://github.com/material-components/material-components-flutter-motion-codelab.git cd material-components-flutter-motion-codelab
2. Seçenek: Başlangıç codelab uygulamasının posta dosyasını indirin
Başlangıç uygulaması material-components-flutter-motion-codelab-starter
dizinindedir.
Proje bağımlılıklarını doğrulama
Proje, animasyon paketine bağlıdır. pubspec.yaml
dokümanındaki dependencies
bölümünde aşağıdakilerin bulunduğuna dikkat edin:
animations: ^2.0.0
Projeyi açın ve uygulamayı çalıştırın
- Projeyi istediğiniz düzenleyicide açın.
- Seçtiğiniz düzenleyici için Başlarken: Deneme sürümü bölümündeki "Uygulamayı çalıştırma" talimatlarını uygulayın.
Başarıyla gerçekleştirildi. Reply'in ana sayfası için başlatıcı kod, cihazınızda/emülatörünüzde çalışmalıdır. E-posta listesinin yer aldığı Gelen Kutusu gösterilir.
İsteğe bağlı: Cihaz animasyonlarını yavaşlatma
Bu kod laboratuvarı hızlı ancak şık geçişler içerdiğinden, geçişleri uygularken geçişlerin bazı ayrıntılarını gözlemlemek için cihazın animasyonlarını yavaşlatmanız yararlı olabilir. Bunu, alt çekmece açıkken ayarlar simgesine dokunarak erişebileceğiniz bir uygulama içi ayar kullanarak yapabilirsiniz. Endişelenmeyin, cihaz animasyonlarını yavaşlatmaya yönelik bu yöntem, Yanıtla uygulamasının dışında cihazdaki animasyonları etkilemez.
İsteğe bağlı: Koyu Mod
Yanıt özelliğinin parlak teması gözlerinizi acıyorsa başka yere yardım etmeyin. Uygulama içinde bulunan bir ayar sayesinde uygulama temasını koyu moda çevirerek gözlerinize daha uygun hale getirebilirsiniz. Bu ayara, alt çekmece açıkken ayarlar simgesine dokunarak erişebilirsiniz.
4. Örnek uygulama kodu hakkında bilgi edinin
Koda bakalım. Uygulamadaki farklı ekranlar arasında geçiş yapmak için animasyon paketini kullanan bir uygulama sağladık.
- HomePage: Seçilen posta kutusunu görüntüler
- InboxPage: E-postaların listesini gösterir.
- MailPreviewCard: E-postanın önizlemesini gösterir.
- MailViewPage: Tek bir e-postayı tam olarak gösterir.
- ComposePage: Yeni bir e-posta oluşturmanıza olanak tanır.
- SearchPage: Bir arama görünümünü görüntüler
router.dart
İlk olarak, uygulamanın kök gezinme işlevinin nasıl ayarlandığını anlamak için lib
dizininde router.dart
dosyasını açın:
class ReplyRouterDelegate extends RouterDelegate<ReplyRoutePath>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<ReplyRoutePath> {
ReplyRouterDelegate({required this.replyState})
: navigatorKey = GlobalObjectKey<NavigatorState>(replyState) {
replyState.addListener(() {
notifyListeners();
});
}
@override
final GlobalKey<NavigatorState> navigatorKey;
RouterProvider replyState;
@override
void dispose() {
replyState.removeListener(notifyListeners);
super.dispose();
}
@override
ReplyRoutePath get currentConfiguration => replyState.routePath!;
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<RouterProvider>.value(value: replyState),
],
child: Selector<RouterProvider, ReplyRoutePath?>(
selector: (context, routerProvider) => routerProvider.routePath,
builder: (context, routePath, child) {
return Navigator(
key: navigatorKey,
onPopPage: _handlePopPage,
pages: [
// TODO: Add Shared Z-Axis transition from search icon to search view page (Motion)
const CustomTransitionPage(
transitionKey: ValueKey('Home'),
screen: HomePage(),
),
if (routePath is ReplySearchPath)
const CustomTransitionPage(
transitionKey: ValueKey('Search'),
screen: SearchPage(),
),
],
);
},
),
);
}
bool _handlePopPage(Route<dynamic> route, dynamic result) {
// _handlePopPage should not be called on the home page because the
// PopNavigatorRouterDelegateMixin will bubble up the pop to the
// SystemNavigator if there is only one route in the navigator.
assert(route.willHandlePopInternally ||
replyState.routePath is ReplySearchPath);
final bool didPop = route.didPop(result);
if (didPop) replyState.routePath = const ReplyHomePath();
return didPop;
}
@override
Future<void> setNewRoutePath(ReplyRoutePath configuration) {
replyState.routePath = configuration;
return SynchronousFuture<void>(null);
}
}
Bu kök gezinme aracımızdır ve uygulamamızın tuvalin tamamını kaplayan HomePage
ve SearchPage
gibi ekranlarını yönetir. Araç, uygulamamızın durumunu dinleyerek ReplySearchPath
rotasını belirleyip belirlemediğimizi kontrol eder. Varsa gezginimizi yığının en üstündeki SearchPage
ile yeniden oluşturur. Ekranlarımızın, geçiş tanımlanmamış bir CustomTransitionPage
içine sarıldığını görebilirsiniz. Bu görselde, özel geçiş olmadan ekranlar arasında gezinmenin bir yolu gösterilmektedir.
home.dart
home.dart
içindeki _BottomAppBarActionItems
içinde aşağıdakileri yaparak rotamızı uygulamamızın durumunda ReplySearchPath
olarak belirledik:
Align(
alignment: AlignmentDirectional.bottomEnd,
child: IconButton(
icon: const Icon(Icons.search),
color: ReplyColors.white50,
onPressed: () {
Provider.of<RouterProvider>(
context,
listen: false,
).routePath = const ReplySearchPath();
},
),
);
onPressed
parametremizde RouterProvider
'ımıza erişip routePath
değerini ReplySearchPath
olarak ayarlıyoruz. RouterProvider
uygulamamız, kök gezginlerin durumunu izler.
mail_view_router.dart
Şimdi, uygulamamızın iç gezinme menüsünün nasıl ayarlandığına bakalım. lib
dizininde mail_view_router.dart
uygulamasını açın. Yukarıdakine benzer bir gezgin görürsünüz:
class MailViewRouterDelegate extends RouterDelegate<void>
with ChangeNotifier, PopNavigatorRouterDelegateMixin {
MailViewRouterDelegate({required this.drawerController});
final AnimationController drawerController;
@override
Widget build(BuildContext context) {
bool _handlePopPage(Route<dynamic> route, dynamic result) {
return false;
}
return Selector<EmailStore, String>(
selector: (context, emailStore) => emailStore.currentlySelectedInbox,
builder: (context, currentlySelectedInbox, child) {
return Navigator(
key: navigatorKey,
onPopPage: _handlePopPage,
pages: [
// TODO: Add Fade through transition between mailbox pages (Motion)
CustomTransitionPage(
transitionKey: ValueKey(currentlySelectedInbox),
screen: InboxPage(
destination: currentlySelectedInbox,
),
)
],
);
},
);
}
...
}
Bu, iç navigasyon cihazımızdır. Uygulamamızın, InboxPage
gibi yalnızca kanvasın gövdesini kaplayan iç ekranlarını işler. InboxPage
, uygulamamızın durumundaki mevcut posta kutusuna bağlı olarak e-postaların listesini gösterir. Uygulamamızın durumunun currentlySelectedInbox
özelliğinde bir değişiklik olduğunda, gezinme grubu, yığının üst kısmında doğru InboxPage
olacak şekilde yeniden oluşturulur.
home.dart
home.dart
içinde _HomePageState
içinde aşağıdakileri yaparak mevcut posta kutumuzu uygulamamızın durumuna göre ayarladık:
void _onDestinationSelected(String destination) {
var emailStore = Provider.of<EmailStore>(
context,
listen: false,
);
if (emailStore.onMailView) {
emailStore.currentlySelectedEmailId = -1;
}
if (emailStore.currentlySelectedInbox != destination) {
emailStore.currentlySelectedInbox = destination;
}
setState(() {});
}
_onDestinationSelected
işlevimizde, EmailStore
işlevimize erişiyor ve currentlySelectedInbox
değerini seçilen hedefe ayarlıyoruz. EmailStore
, iç gezginimizin durumunu izler.
home.dart
Son olarak, kullanılan gezinme yönlendirmesinin bir örneğini görmek için lib
dizininde home.dart
dosyasını açın. InkWell
widget'ının onTap
özelliğinde _ReplyFabState
sınıfını bulun. Bu özellik aşağıdaki gibi görünür:
onTap: () {
Provider.of<EmailStore>(
context,
listen: false,
).onCompose = true;
Navigator.of(context).push(
PageRouteBuilder(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return const ComposePage();
},
),
);
},
Bu görsel, herhangi bir özel geçiş olmadan e-posta oluşturma sayfasına nasıl gidebileceğinizi gösterir. Bu codelab'de, uygulamadaki çeşitli gezinme işlemleriyle birlikte çalışan Material geçişleri ayarlamak için Reply'in koduna göz atacaksınız.
Artık başlangıç kodunu öğrendiğinize göre ilk geçişimizi uygulayalım.
5. E-posta listesinden e-posta ayrıntıları sayfasına kapsayıcı dönüşümü geçişi ekleyin
Başlamak için bir e-postayı tıklayarak geçiş ekleyeceksiniz. Kapsayıcı dönüşüm deseni, kapsayıcı içeren kullanıcı arayüzü öğeleri arasındaki geçişler için tasarlandığından bu gezinme değişikliği için çok uygundur. Bu desen, iki kullanıcı arayüzü öğesi arasında görünür bir bağlantı oluşturur.
Herhangi bir kod eklemeden önce, Yanıtla uygulamasını çalıştırmayı ve bir e-postayı tıklamayı deneyin. Sıçramalı kesme sayesinde ekran, geçiş olmadan değiştirilir:
Önce
Aşağıdaki snippet'te gösterildiği gibi, mail_card_preview.dart
dosyasının en üstüne animasyon paketi için bir içe aktarma işlemi ekleyerek başlayın:
mail_card_preview.dart
import 'package:animations/animations.dart';
Animasyon paketini içe aktardığınıza göre uygulamanıza güzel geçişler eklemeye başlayabiliriz. OpenContainer
widget'ımızı barındıracak bir StatelessWidget
sınıfı oluşturarak başlayalım.
mail_card_preview.dart
dosyasında, MailPreviewCard
sınıf tanımından sonra aşağıdaki kod snippet'ini ekleyin:
mail_card_preview.dart
// TODO: Add Container Transform transition from email list to email detail page (Motion)
class _OpenContainerWrapper extends StatelessWidget {
const _OpenContainerWrapper({
required this.id,
required this.email,
required this.closedChild,
});
final int id;
final Email email;
final Widget closedChild;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return OpenContainer(
openBuilder: (context, closedContainer) {
return MailViewPage(id: id, email: email);
},
openColor: theme.cardColor,
closedShape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(0)),
),
closedElevation: 0,
closedColor: theme.cardColor,
closedBuilder: (context, openContainer) {
return InkWell(
onTap: () {
Provider.of<EmailStore>(
context,
listen: false,
).currentlySelectedEmailId = id;
openContainer();
},
child: closedChild,
);
},
);
}
}
Şimdi yeni sarmalayıcımızı kullanalım. MailPreviewCard
sınıf tanımının içinde, build()
işlevimizdeki Material
widget'ını yeni _OpenContainerWrapper
ile sarmalayacağız:
mail_card_preview.dart
// TODO: Add Container Transform transition from email list to email detail page (Motion)
return _OpenContainerWrapper(
id: id,
email: email,
closedChild: Material(
...
_OpenContainerWrapper
öğesimizin bir InkWell
widget'ı vardır ve OpenContainer
öğesinin renk özellikleri, içinde bulunduğu kapsayıcının rengini tanımlar. Bu nedenle, Material ve Mürekkep Damlası widget'larını kaldırabiliriz. Elde edilen kod aşağıdaki gibi görünür:
mail_card_preview.dart
// TODO: Add Container Transform transition from email list to email detail page (Motion)
return _OpenContainerWrapper(
id: id,
email: email,
closedChild: Dismissible(
key: ObjectKey(email),
dismissThresholds: const {
DismissDirection.startToEnd: 0.8,
DismissDirection.endToStart: 0.4,
},
onDismissed: (direction) {
switch (direction) {
case DismissDirection.endToStart:
if (onStarredInbox) {
onStar();
}
break;
case DismissDirection.startToEnd:
onDelete();
break;
default:
}
},
background: _DismissibleContainer(
icon: 'twotone_delete',
backgroundColor: colorScheme.primary,
iconColor: ReplyColors.blue50,
alignment: Alignment.centerLeft,
padding: const EdgeInsetsDirectional.only(start: 20),
),
confirmDismiss: (direction) async {
if (direction == DismissDirection.endToStart) {
if (onStarredInbox) {
return true;
}
onStar();
return false;
} else {
return true;
}
},
secondaryBackground: _DismissibleContainer(
icon: 'twotone_star',
backgroundColor: currentEmailStarred
? colorScheme.secondary
: theme.scaffoldBackgroundColor,
iconColor: currentEmailStarred
? colorScheme.onSecondary
: colorScheme.onBackground,
alignment: Alignment.centerRight,
padding: const EdgeInsetsDirectional.only(end: 20),
),
child: mailPreview,
),
);
Bu aşamada, tamamen çalışan bir kapsayıcı dönüştürme işleminiz olmalıdır. Bir e-postayı tıkladığınızda, e-posta listesi çıkartılırken liste öğesi bir ayrıntılar ekranına genişletilir. Geri tuşuna bastığınızda e-posta ayrıntıları ekranı tekrar bir liste öğesine daraltılırken e-posta listesinde yukarı ölçeklenir.
Sonra
6. FAB'den e-posta oluşturma sayfasına Kapsayıcı Dönüşümü geçişi ekleme
Şimdi, container dönüşümüne devam edelim ve Kayan İşlem Düğmesi'nden ComposePage
öğesine bir geçiş ekleyerek FAB'yi kullanıcı tarafından yazılacak yeni bir e-posta adresine genişletelim. Öncelikle uygulamayı yeniden çalıştırın ve e-posta oluşturma ekranı açıldığında geçiş olmadığını görmek için FAB'ı tıklayın.
Önce
Aynı widget sınıfını (OpenContainer
) kullandığımız için bu geçişi yapılandırma şeklimiz, önceki adımda yaptığımıza çok benzer olacaktır.
home.dart
ürününde, dosyanın en üstündeki package:animations/animations.dart
öğesini içe aktaralım ve _ReplyFabState
build()
yöntemini değiştirelim. Döndürülen Material
widget'ını bir OpenContainer
widget'ı ile saralım:
home.dart
// TODO: Add Container Transform from FAB to compose email page (Motion)
return OpenContainer(
openBuilder: (context, closedContainer) {
return const ComposePage();
},
openColor: theme.cardColor,
onClosed: (success) {
Provider.of<EmailStore>(
context,
listen: false,
).onCompose = false;
},
closedShape: circleFabBorder,
closedColor: theme.colorScheme.secondary,
closedElevation: 6,
closedBuilder: (context, openContainer) {
return Material(
color: theme.colorScheme.secondary,
...
Önceki OpenContainer
widget'ımızı yapılandırmak için kullanılan parametrelere ek olarak, onClosed
de ayarlanıyor. onClosed
, OpenContainer
rotası açıldığında veya kapalı duruma döndüğünde çağrılan bir ClosedCallback
. Bu işlemin döndürdüğü değer, bu işleve bağımsız değişken olarak iletilir. Bu Callback
numarasını, uygulama sağlayıcımızı ComposePage
rotasından ayrıldığımızı bildirmek için kullanıyoruz. Bu şekilde tüm dinleyicilere bilgi verebiliyoruz.
Son adımımızda yaptığımıza benzer şekilde, OpenContainer
widget'ı closedColor
ile closedBuilder
tarafından döndürülen widget'ın rengini işlediği için Material
widget'ını widget'ımızdan kaldıracağız. Ayrıca, InkWell widget'ımızın onTap
içindeki Navigator.push()
çağrımızı kaldırıp OpenContainer
widget'ının closedBuilder
tarafından verilen openContainer() Callback
ile değiştireceğiz. Artık OpenContainer
widget'ı kendi yönlendirmesini yapıyor.
Elde edilen kod aşağıdaki gibidir:
home.dart
// TODO: Add Container Transform from FAB to compose email page (Motion)
return OpenContainer(
openBuilder: (context, closedContainer) {
return const ComposePage();
},
openColor: theme.cardColor,
onClosed: (success) {
Provider.of<EmailStore>(
context,
listen: false,
).onCompose = false;
},
closedShape: circleFabBorder,
closedColor: theme.colorScheme.secondary,
closedElevation: 6,
closedBuilder: (context, openContainer) {
return Tooltip(
message: tooltip,
child: InkWell(
customBorder: circleFabBorder,
onTap: () {
Provider.of<EmailStore>(
context,
listen: false,
).onCompose = true;
openContainer();
},
child: SizedBox(
height: _mobileFabDimension,
width: _mobileFabDimension,
child: Center(
child: fabSwitcher,
),
),
),
);
},
);
Şimdi bazı eski kodları temizleyeceğiz. OpenContainer
widget'ımız artık uygulamamızın sağlayıcısını onClosed ClosedCallback
üzerinden ComposePage
'da olmadığımızı bildirmekle ilgilendiği için mail_view_router.dart
'daki önceki uygulamamızı kaldırabiliriz:
mail_view_router.dart
// TODO: Add Container Transform from FAB to compose email page (Motion)
emailStore.onCompose = false; /// delete this line
return SynchronousFuture<bool>(true);
Bu adımda bu kadar. FAB'den oluşturma ekranına geçişiniz aşağıdaki gibi görünmelidir:
Sonra
7. Arama simgesinden arama görünümü sayfasına paylaşılan z ekseni geçişi ekleme
Bu adımda, arama simgesinden tam ekran arama görünümüne geçiş animasyonu ekleyeceğiz. Bu gezinme değişikliğine dahil olan kalıcı bir kapsayıcı olmadığından, iki ekran arasındaki uzamsal ilişkiyi güçlendirmek ve uygulamanın hiyerarşisinde bir seviye yukarı hareket ettiğini göstermek için Paylaşılan Z ekseni geçişini kullanabiliriz.
Ek kod eklemeden önce uygulamayı çalıştırıp ekranın sağ alt köşesindeki arama simgesine dokunmayı deneyin. Bu işlem, geçiş olmadan arama görünümü ekranını getirir.
Önce
Başlamak için router.dart
dosyamıza gidelim. ReplySearchPath
sınıf tanımımızdan sonra aşağıdaki snippet'i ekleyin:
router.dart
// TODO: Add Shared Z-Axis transition from search icon to search view page (Motion)
class SharedAxisTransitionPageWrapper extends Page {
const SharedAxisTransitionPageWrapper(
{required this.screen, required this.transitionKey})
: super(key: transitionKey);
final Widget screen;
final ValueKey transitionKey;
@override
Route createRoute(BuildContext context) {
return PageRouteBuilder(
settings: this,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return SharedAxisTransition(
fillColor: Theme.of(context).cardColor,
animation: animation,
secondaryAnimation: secondaryAnimation,
transitionType: SharedAxisTransitionType.scaled,
child: child,
);
},
pageBuilder: (context, animation, secondaryAnimation) {
return screen;
});
}
}
Şimdi, istediğimiz geçişi gerçekleştirmek için yeni SharedAxisTransitionPageWrapper
aracımızı kullanalım. ReplyRouterDelegate
sınıf tanımı içinde, pages
mülkünün altında arama ekranımızı CustomTransitionPage
yerine SharedAxisTransitionPageWrapper
ile saralım:
router.dart
return Navigator(
key: navigatorKey,
onPopPage: _handlePopPage,
pages: [
// TODO: Add Shared Z-Axis transition from search icon to search view page (Motion)
const CustomTransitionPage(
transitionKey: ValueKey('Home'),
screen: HomePage(),
),
if (routePath is ReplySearchPath)
const SharedAxisTransitionPageWrapper(
transitionKey: ValueKey('Search'),
screen: SearchPage(),
),
],
);
Şimdi uygulamayı yeniden çalıştırmayı deneyin.
Her şey yolunda gitmeye başlıyor. Alt uygulama çubuğundaki arama simgesini tıkladığınızda, ortak eksen geçişi arama sayfasını görüntüye göre ölçeklendirir. Ancak arama sayfası genişledikçe ana sayfanın nasıl ölçeklenmediğini ve sabit kaldığını gösterir. Ayrıca, geri düğmesine basıldığında ana sayfa görünüm olarak ölçeklendirilmez. Arama sayfası görünümden çıktığında ana sayfa statik olarak kalır. Henüz işimiz bitmedi.
HomePage
öğesini CustomTransitionPage
yerine SharedAxisTransitionWrapper
ile sarmalayarak her iki sorunu da düzeltelim:
router.dart
return Navigator(
key: navigatorKey,
onPopPage: _handlePopPage,
pages: [
// TODO: Add Shared Z-Axis transition from search icon to search view page (Motion)
const SharedAxisTransitionPageWrapper(
transitionKey: ValueKey('home'),
screen: HomePage(),
),
if (routePath is ReplySearchPath)
const SharedAxisTransitionPageWrapper(
transitionKey: ValueKey('search'),
screen: SearchPage(),
),
],
);
İşte bu kadar. Şimdi uygulamayı yeniden çalıştırmayı ve arama simgesine dokunmayı deneyin. Ana sayfa ve arama görünümü ekranları aynı anda Z ekseninde ayrıntılı olarak kararma ve ölçeklenmeli, böylece iki ekran arasında kesintisiz bir efekt sağlanmalıdır.
Sonra
8. Posta kutusu sayfaları arasında Şeffaflaştırma geçişi ekleme
Bu adımda, farklı posta kutuları arasında geçiş ekleyeceğiz. Uzamsal veya hiyerarşik bir ilişkiyi vurgulamak istemediğimizden, e-posta listeleri arasında basit bir "değişim" yapmak için geçiş efekti kullanacağız.
Başka kod eklemeden önce uygulamayı çalıştırmayı, alt uygulama çubuğundaki Yanıtla logosuna dokunmayı ve posta kutularını değiştirmeyi deneyin. E-posta listesi, geçiş olmadan değişecektir.
Önce
Başlamak için mail_view_router.dart
dosyamıza gidelim. MailViewRouterDelegate
sınıf tanımımımızın ardından aşağıdaki snippet'i ekleyin:
mail_view_router.dart
// TODO: Add Fade through transition between mailbox pages (Motion)
class FadeThroughTransitionPageWrapper extends Page {
const FadeThroughTransitionPageWrapper({
required this.mailbox,
required this.transitionKey,
}) : super(key: transitionKey);
final Widget mailbox;
final ValueKey transitionKey;
@override
Route createRoute(BuildContext context) {
return PageRouteBuilder(
settings: this,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeThroughTransition(
fillColor: Theme.of(context).scaffoldBackgroundColor,
animation: animation,
secondaryAnimation: secondaryAnimation,
child: child,
);
},
pageBuilder: (context, animation, secondaryAnimation) {
return mailbox;
});
}
}
Son adımımıza benzer şekilde, istediğimiz geçişi gerçekleştirmek için yeni FadeThroughTransitionPageWrapper
özelliğimizi kullanalım. MailViewRouterDelegate
sınıf tanımımızda, pages
özelliği altında, posta kutusu ekranımızı CustomTransitionPage
ile sarmalamak yerine FadeThroughTransitionPageWrapper
kodunu kullanın:
mail_view_router.dart
return Navigator(
key: navigatorKey,
onPopPage: _handlePopPage,
pages: [
// TODO: Add Fade through transition between mailbox pages (Motion)
FadeThroughTransitionPageWrapper(
mailbox: InboxPage(destination: currentlySelectedInbox),
transitionKey: ValueKey(currentlySelectedInbox),
),
],
);
Uygulamayı yeniden çalıştırın. Alt gezinme çekmecesini açıp posta kutularını değiştirdiğinizde, mevcut e-posta listesi kaybolarak genişler ve yeni liste genişler ve genişler. Güzel!
Sonra
9. Oluşturma ve yanıtlama FAB'si arasında Şeffaflaştırma geçişi ekle
Bu adımda farklı FAB simgeleri arasında bir geçiş ekleyeceğiz. Uzamsal veya hiyerarşik bir ilişkiyi vurgulamak istemediğimizden, FAB'daki simgeler arasında basit bir "değişim" yapmak için karartma yöntemini kullanacağız.
Başka kod eklemeden önce bir e-postaya dokunup e-posta görünümünü açarak uygulamayı çalıştırmayı deneyin. FAB simgesi geçiş olmadan değişmelidir.
Önce
Codelab'in geri kalanında home.dart
üzerinde çalışacağız. Bu nedenle, 2. adımda home.dart
için zaten yaptığımızdan animasyon paketi için içe aktarmayı ekleme konusunda endişelenmeyin.
Sonraki birkaç geçişin tümü yeniden kullanılabilir bir sınıftan (_FadeThroughTransitionSwitcher
) yararlanacağı için yapılandırma biçimimiz çok benzer olacaktır.
home.dart
için _ReplyFabState
altına şu snippet'i ekleyelim:
home.dart
// TODO: Add Fade through transition between compose and reply FAB (Motion)
class _FadeThroughTransitionSwitcher extends StatelessWidget {
const _FadeThroughTransitionSwitcher({
required this.fillColor,
required this.child,
});
final Widget child;
final Color fillColor;
@override
Widget build(BuildContext context) {
return PageTransitionSwitcher(
transitionBuilder: (child, animation, secondaryAnimation) {
return FadeThroughTransition(
fillColor: fillColor,
child: child,
animation: animation,
secondaryAnimation: secondaryAnimation,
);
},
child: child,
);
}
}
Ardından _ReplyFabState
'te fabSwitcher
widget'ını bulun. fabSwitcher
, e-posta görünümünde olup olmadığına bağlı olarak farklı bir simge döndürür. _FadeThroughTransitionSwitcher
ile toparlayalım:
home.dart
// TODO: Add Fade through transition between compose and reply FAB (Motion)
static final fabKey = UniqueKey();
static const double _mobileFabDimension = 56;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final circleFabBorder = const CircleBorder();
return Selector<EmailStore, bool>(
selector: (context, emailStore) => emailStore.onMailView,
builder: (context, onMailView, child) {
// TODO: Add Fade through transition between compose and reply FAB (Motion)
final fabSwitcher = _FadeThroughTransitionSwitcher(
fillColor: Colors.transparent,
child: onMailView
? Icon(
Icons.reply_all,
key: fabKey,
color: Colors.black,
)
: const Icon(
Icons.create,
color: Colors.black,
),
);
...
_FadeThroughTransitionSwitcher
için şeffaf bir fillColor
sunuyoruz. Böylece geçiş sırasında öğeler arasında arka plan olmayacak. Ayrıca bir UniqueKey
oluşturup simgelerden birine atarız.
Bu adımda, bağlama dayalı ve tamamen animasyonlu bir FAB'niz olmalıdır. E-posta görünümüne geçtiğinizde eski FAB simgesi şeffaflaşır ve küçültür, yenisi ise şeffaflaşır ve büyür.
Sonra
10. Kaybolan posta kutusu başlığı arasındaki Şeffaflaştırma geçişi ekleme
Bu adımda, e-posta görünümündeyken posta kutusu başlığını görünür ve görünmez durum arasında soldan sağa doğru kaydırarak göstermek için bir soldan sağa kaydırma geçişi ekleyeceğiz. Uzamsal veya hiyerarşik bir ilişkiyi vurgulamak istemediğimizden, posta kutusu başlığını kapsayan Text
widget'ı ile boş bir SizedBox
arasında basit bir "değişim" yapmak için geçiş efekti kullanacağız.
Ek kod eklemeden önce uygulamayı çalıştırmayı, bir e-postaya dokunup e-posta görünümünü açmayı deneyin. Posta kutusu başlığı, geçiş olmadan kaybolacaktır.
Önce
Son adımımızda _FadeThroughTransitionSwitcher
özelliğindeki işin çoğunu zaten gerçekleştirdiğimiz için bu codelab'in geri kalanı hızlı olacak.
Şimdi, geçişimizi eklemek için home.dart
uygulamasındaki _AnimatedBottomAppBar
dersimize gidelim. Son adımımızdan itibaren _FadeThroughTransitionSwitcher
yeniden kullanılacak ve boş bir SizedBox
döndüren onMailView
koşulumuzu sarmalayacağız veya alt çekmeceyle senkronize olarak kaybolan bir posta kutusu başlığı döndüreceğiz:
home.dart
...
const _ReplyLogo(),
const SizedBox(width: 10),
// TODO: Add Fade through transition between disappearing mailbox title (Motion)
_FadeThroughTransitionSwitcher(
fillColor: Colors.transparent,
child: onMailView
? const SizedBox(width: 48)
: FadeTransition(
opacity: fadeOut,
child: Selector<EmailStore, String>(
selector: (context, emailStore) =>
emailStore.currentlySelectedInbox,
builder: (
context,
currentlySelectedInbox,
child,
) {
return Text(
currentlySelectedInbox,
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
color: ReplyColors.white50,
),
);
},
),
),
),
Hepsi bu kadar, bu adımı da tamamladık.
Uygulamayı yeniden çalıştırın. Bir e-postayı açıp e-posta görünümüne yönlendirildiğinizde, alt uygulama çubuğundaki posta kutusu başlığı şeffaf bir şekilde belirir ve genişler. Mükemmel!
Sonra
11. Alt uygulama çubuğu işlemleri arasında şeffaflaşma geçişi ekleme
Bu adımda, uygulamaların bağlamına göre alt uygulama çubuğu işlemlerini yavaş yavaş göstermek için bir karartma geçişi ekleyeceğiz. Üç boyutlu veya hiyerarşik bir ilişkiyi vurgulamak istemediğimizden, uygulama Ana Sayfa'da, alt çekmece görünür olduğunda ve e-posta görünümündeyken alt uygulama çubuğu işlemleri arasında basit bir "değişim" yapmak için geçiş efekti kullanacağız.
Ek kod eklemeden önce uygulamayı çalıştırmayı, bir e-postaya dokunup e-posta görünümünü açmayı deneyin. Yanıtla logosuna dokunmayı da deneyebilirsiniz. Alt uygulama çubuğundaki işlemler geçiş olmadan değişmelidir.
Önce
Son adıma benzer şekilde, _FadeThroughTransitionSwitcher
aracımızı tekrar kullanacağız. İstenen geçişi elde etmek için _BottomAppBarActionItems
sınıf tanımımıza gidin ve build()
işlevimizin dönüş widget'ını bir _FadeThroughTransitionSwitcher
ile sarmalayın:
home.dart
// TODO: Add Fade through transition between bottom app bar actions (Motion)
return _FadeThroughTransitionSwitcher(
fillColor: Colors.transparent,
child: drawerVisible
? Align(
key: UniqueKey(),
alignment: AlignmentDirectional.bottomEnd,
child: IconButton(
icon: const Icon(Icons.settings),
color: ReplyColors.white50,
onPressed: () async {
drawerController.reverse();
showModalBottomSheet(
context: context,
shape: RoundedRectangleBorder(
borderRadius: modalBorder,
),
builder: (context) => const SettingsBottomSheet(),
);
},
),
)
: onMailView
...
Şimdi deneyelim. Bir e-postayı açıp e-posta görünümüne yönlendirildiğinizde eski alt uygulama çubuğu işlemleri, yeni işlemler kademeli olarak artarken ve genişlerken kaybolmalıdır. Tebrikler!
Sonra
12. Tebrikler!
100 satırdan kısa bir Dart kodu kullanarak animasyon paketi, mevcut bir uygulamada Material Design yönergelerine uygun ve tüm cihazlarda tutarlı bir görünüm ve davranışa sahip güzel geçişler oluşturmanıza yardımcı oldu.
Sonraki adımlar
Materyal hareket sistemi hakkında daha fazla bilgi edinmek için yönergeleri ve geliştirici dokümanlarının tamamını incelediğinizden emin olun ve uygulamanıza bazı Materyal geçişler eklemeyi deneyin!
Material Motion'u denediğiniz için teşekkür ederiz. Bu codelab'den keyif aldığınızı umuyoruz.
Bu codelab'i makul bir zaman ve çabayla tamamlayabildim
Gelecekte Materyal hareket sistemini kullanmaya devam etmek istiyorum
Flutter Gallery'ye göz atın
Material Flutter kitaplığı ve Flutter çerçevesi tarafından sağlanan widget'ları nasıl kullanacağınızla ilgili daha fazla demo için Flutter Gallery'yi ziyaret etmeyi unutmayın. |