1. Giriş
Materyal Tasarım, cesur ve güzel dijital ürünler oluşturmaya yönelik bir sistemdir. Ürün ekipleri stil, marka, etkileşim ve hareketi tutarlı bir ilke ve bileşen kümesi altında bir araya getirerek en büyük tasarım potansiyellerini gerçekleştirebilir.
Materyal Bileşenleri (MDC), geliştiricilerin Materyal Tasarım'ı uygulamasına yardımcı olur. Google'da mühendislerden ve kullanıcı deneyimi tasarımcılarından oluşan bir ekip tarafından oluşturulan MDC, onlarca güzel ve işlevsel kullanıcı arayüzü bileşeni içerir. Ayrıca Android, iOS, web ve Flutter.material.io/develop'da kullanılabilir. |
Flutter için Material'ın 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ındaki geçişler; 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: Mekansal 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 paylaşılan bir dönüşüm kullanır.
- Karartma: Birbiriyle güçlü bir ilişkisi olmayan kullanıcı arayüzü öğeleri arasındaki geçişler; ardışık bir belirme ve belirme kullanır.
- Karart: Ekranın sınırları içinde giren veya çı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 materyal 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'de, uygulamanızın görünümünü ve tarzını özelleştirmek için animasyon paketindeki geçişleri nasıl kullanabileceğinizi göstermek üzere Dart kullanarak Reply adlı örnek bir Flutter e-posta uygulamasına bazı geçişler yapabilirsiniz.
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ü'nün e-posta listesinden e-posta ayrıntıları sayfasına geçiş
- FAB'dan e-posta oluşturma sayfasına Kapsayıcı Dönüşümü geçişi
- Arama simgesinden arama görünümü sayfasına paylaşılan Z ekseni geçişi
- Posta kutusu sayfaları arasında Kararma geçişi
- Oluşturma ve yanıtlama FAB'si arasında Kararma geçişi
- Kaybolan posta kutusu başlıkları arasında Kararma 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 konusundaki deneyim düzeyinizi nasıl değerlendirirsiniz?
Bu codelab'den ne öğrenmek istersiniz?
2. Flutter geliştirme ortamınızı kurma
Bu laboratuvarı tamamlamak için iki yazılıma ihtiyacınız vardır: Flutter SDK'sı ve düzenleyici.
Codelab'i aşağıdaki cihazlardan birini kullanarak çalıştırabilirsiniz:
- Bilgisayarınıza bağlı ve Geliştirici moduna ayarlanmış fiziksel bir Android veya iOS cihaz.
- iOS simülatörü (Xcode araçlarının yüklenmesini gerektirir).
- Android Emülatör (Android Studio'da kurulum gerektirir).
- Tarayıcı (hata ayıklama için Chrome gereklidir).
- 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. İşletim sistemine özgü gereksinimler docs.flutter.dev/desktop sayfasında ayrıntılı olarak açıklanmıştır.
3. codelab başlangıç uygulamasını indirme
1. seçenek: GitHub'dan başlangıç codelab uygulamasını klonlama
Bu codelab'i GitHub'dan klonlamak için şu 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.
- "Uygulamayı çalıştırma" talimatlarını izleyin Başlarken: Seçtiğiniz düzenleyici için uygulamayı test edin.
Başarıyla gerçekleştirildi. Yanıt'ın ana sayfası için başlangıç kodu, 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 codelab'de hızlı ve gösterişli geçişler yer aldığından, uyguladığınız geçişlerin daha ince ayrıntılarını gözlemlemek için cihazın animasyonlarını yavaşlatmanız yararlı olabilir. Bunu uygulama içi bir ayar kullanarak yapabilirsiniz. Alt çekmece açıkken ayarlar simgesine dokunarak erişebilirsiniz. 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: Bir e-postanın önizlemesini gösterir
- MailViewPage: Tek bir tam e-posta gösterir
- ComposePage: Yeni bir e-postanın oluşturulmasına 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, yığınımızın en üstündeki SearchPage
ile gezginimizi yeniden oluşturur. Ekranlarımızın, hiçbir geçiş tanımlanmadan bir CustomTransitionPage
içine yerleştirildiğine dikkat edin. Bu, herhangi bir özel geçiş olmadan ekranlar arasında gezinebileceğiniz bir yol gösterir.
home.dart
home.dart
içinde _BottomAppBarActionItems
içinde şunu yaparak rotamızı uygulamamızın durumunda ReplySearchPath
olarak ayarladık:
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
değerine erişiyor ve 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 kılavuz 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 bizim içimizdeki gezgin. Uygulamamızın, InboxPage
gibi yalnızca kanvasın gövdesini kaplayan iç ekranlarını işler. InboxPage
, uygulamamızın durumunda bulunan posta kutusunun ne olduğuna bağlı olarak bir e-posta listesi görüntüler. Uygulamamızın durumunun currentlySelectedInbox
özelliğinde bir değişiklik olduğunda, gezgin, 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
fonksiyonumuzda, EmailStore
işlevimize erişiyor ve currentlySelectedInbox
değerini seçilen hedefe ayarlıyoruz. EmailStore
aracımız, içindeki gezginlerin durumunu takip eder.
home.dart
Son olarak, kullanılan gezinme yönlendirmesinin bir örneğini görmek için lib
dizininde home.dart
uygulaması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, uygulama genelinde çeşitli gezinme işlemleriyle birlikte çalışan Materyal geçişleri ayarlamak için Yanıt'ın kodunu ayrıntılı olarak inceleyeceksiniz.
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. Bu gezinme değişikliği için kapsayıcı dönüşüm kalıbı, kapsayıcı içeren kullanıcı arayüzü öğeleri arasındaki geçişler için tasarlandığından ç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
üst kısmındaki animasyon paketi için içe aktarma işlemi ekleyerek başlayın:
mail_card_preview.dart
import 'package:animations/animations.dart';
Artık animasyon paketinizi 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
öğesinde, 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 Inkwell widget'larını kaldırabiliriz. Bu işlem sonucunda 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, tam olarak çalışan bir container dönüşümünüz 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 öğesi haline getirilirken e-posta listesinde yukarı ölçeklenir.
Sonra
6. E-posta oluşturma sayfasına, FAB'dan Kapsayıcı Dönüşümü geçişi ekleyin
Ş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ını başlatırken herhangi bir geçiş olmadığını görmek için FAB'yi tıklayın.
Önce
Aynı widget sınıfını (OpenContainer
) kullandığımızdan, bu geçişi yapılandırma yöntemimiz son adımda yaptığımız yönteme çok benzeyecektir.
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 sarmalayalı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
. Söz konusu işlemin döndürülen değeri 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ırız. Bu sayede tüm dinleyiciler bilgilendirilir.
Son adımda yaptığımıza benzer şekilde, OpenContainer
widget'ı closedBuilder
tarafından döndürülen widget'ın rengini closedColor
ile işlediğinden Material
widget'ını widget'ımızdan kaldıracağız. Ayrıca, InkWell widget'ınızın onTap
içindeki Navigator.push()
çağrımızı kaldırıp OpenContainer
widget'ı kendi yönlendirmesini yönettiğinden bunu OpenContainer
widget'ının closedBuilder
tarafından verilen openContainer() Callback
ile değiştireceğiz.
Elde edilen kod aşağıdaki gibi olur:
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 biraz eski kodları temizleyin. OpenContainer
widget'ımız artık onClosed ClosedCallback
aracılığıyla uygulama sağlayıcımızı ComposePage
uygulamasında olmadığımızı bildirme işlemini gerçekleştirdiğinden, mail_view_router.dart
üzerindeki eski 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ımın sonuna geldik! FAB'dan oluşturma ekranına geçişiniz aşağıdaki gibi görünür:
Sonra
7. Arama simgesinden arama görünümü sayfasına paylaşılan Z ekseni geçişi ekle
Bu adımda, arama simgesinden tam ekran arama görünümüne bir geçiş 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ırmayı ve ekranın sağ alt köşesindeki arama simgesine dokunmayı deneyin. Bunu yaptığınızda, geçiş olmadan arama görünümü ekranı görüntülenir.
Ö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ımızda, pages
özelliğinin altında, arama ekranımızı CustomTransitionPage
yerine SharedAxisTransitionPageWrapper
ile sarmalayalı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, paylaşılan bir eksen geçişi arama sayfasını ölçeklendirir. Ancak arama sayfası genişledikçe ana sayfanın nasıl ölçeklendirilmediğine ve sabit kaldığına dikkat edin. Ayrıca, geri düğmesine basıldığında ana sayfa görünüm olarak ölçeklenmez. Bunun yerine, arama sayfası görünümden çıktığında statik 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 bir geçiş ekleyeceğiz. Uzamsal veya hiyerarşik bir ilişkiyi vurgulamak istemediğimizden, basit bir "değiştirme" işlemi yapmak için e-posta listeleri arasında geçiş yapın.
Başka kod eklemeden önce uygulamayı çalıştırmayı, Alt Uygulama Çubuğu'ndaki Yanıtla logosuna dokunmayı ve posta kutularını değiştirmeyi deneyin. E-posta listesi geçiş işlemi olmadan değişir.
Önce
Başlamak için mail_view_router.dart
dosyamıza gidelim. MailViewRouterDelegate
sınıf tanımımızdan sonra 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
aracımızı 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. Alttaki 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, basit bir "değiştirme" işlemi yapmak için simgelerinin arasına yerleştirin.
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,
);
}
}
Şimdi, _ReplyFabState
içinde fabSwitcher
widget'ını arayın. 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.
Şimdi bu adımda, tamamen animasyonlu bir bağlama dayalı FAB'nizin olması gerekir. Bir e-posta görünümüne geçtiğinizde, yeni simge belirip genişledikçe eski FAB simgesi kaybolup genişleyecektir.
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ın görünür ve görünmez durumlar arasında geçiş yapması için bir geçiş geçişi ekleyeceğiz. Uzamsal veya hiyerarşik bir ilişkiyi vurgulamak istemediğimizden, basit bir "değiştirme" işlemi yapmak için posta kutusu başlığını kapsayan Text
widget'ı ile boş bir SizedBox
arasında.
Başka kod eklemeden önce bir e-postaya dokunup e-posta görünümünü açarak uygulamayı çalıştırmayı 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ığı soluklanır ve genişler. Mükemmel!
Sonra
11. Alt uygulama çubuğu işlemleri arasında Şeffaflaştırma geçişi ekle
Bu adımda, uygulama bağlamına göre alt uygulama çubuğu işlemlerinde şeffaf bir geçiş için şeffaf geçiş ekleyeceğiz. Uzamsal veya hiyerarşik bir ilişkiyi vurgulamak istemediğimizden, basit bir "değiştirme" işlemi yapmak için ve e-posta görünümündeyken alt uygulama çubuğu işlemleri arasında görünür.
Başka kod eklemeden önce bir e-postaya dokunup e-posta görünümünü açarak uygulamayı çalıştırmayı deneyin. Yanıtla logosuna dokunmayı da deneyebilirsiniz. Alt uygulama çubuğu işlemleri, 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
...
Haydi 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 yavaş yavaş çoğalarak artarken kaybolmalıdır. Tebrikler!
Sonra
12. Tebrikler!
100 satırdan daha az Dart kodu kullanan animasyonlar paketi, mevcut bir uygulamada Materyal Tasarım yönergelerine uyan ve tüm cihazlarda tutarlı bir görünüm ve davranışa sahip harika 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ürler. Bu codelab'den memnun kaldığı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. |