1. Giriş
Materyal Bileşenleri (MDC), geliştiricilerin Materyal Tasarım'ı uygulaması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 |
MDC-103 codelab'inde, Materyal Bileşenlerinin (MDC) rengini, yüksekliğini, tipografisini ve şeklini özelleştirip uygulamanıza stil kazandırdınız.
Materyal Tasarım sistemindeki bir bileşen, bir dizi önceden tanımlanmış görevi yerine getirir ve düğme gibi belirli özelliklere sahiptir. Ancak düğme, kullanıcının bir işlem gerçekleştirmesinin ötesinde bir şeydir. Düğme, kullanıcıya etkileşimli olduğunu ve dokunulduğunda veya tıklandığında bir şey olacağını bildiren şekil, boyut ve renklerin görsel bir ifadesidir.
Materyal Tasarım yönergeleri, bileşenleri bir tasarımcının bakış açısından açıklar. Bu dokümanlar, platformlar genelinde kullanılabilen çok çeşitli temel işlevleri ve her bileşeni oluşturan anatomik öğeleri açıklar. Örneğin, arka plan; arka katmanı ve içeriğini, ön katmanı ve içeriğini, hareket kurallarını ve görüntüleme seçeneklerini içerir. Bu bileşenlerin her biri her uygulamanın ihtiyaçlarına, kullanım alanlarına ve içeriğine göre özelleştirilebilir.
Oluşturacaklarınız
Bu codelab'de Shrine uygulamasındaki kullanıcı arayüzünü, "arka plan" adı verilen iki düzeyli bir sunumla değiştireceksiniz. Arka planda, asimetrik ızgaradaki ürünleri filtrelemek için kullanılan seçilebilir kategorilerin listelendiği bir menü bulunur. Bu codelab'de şunları kullanacaksınız:
- Şekil
- Hareket
- Flutter widget'ları (önceki codelab'lerde kullandığınızlar)
Yapay Zeka | iOS |
Bu codelab'deki Material Flutter bileşenleri ve alt sistemleri
- Şekil
Flutter geliştirme deneyiminizi nasıl değerlendirirsiniz?
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ü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, 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
MDC-103'ten mi devam ediyorsunuz?
MDC-103'ü tamamladıysanız kodunuz bu codelab için hazır olmalıdır. Arka plan menüsünü ekleme adımına atlayın.
Sıfırdan mı başlayacaksınız?
Başlatıcı uygulama material-components-flutter-codelabs-104-starter_and_103-complete/mdc_100_series
dizininde bulunur.
...veya GitHub'dan kopyalayın
Bu kod laboratuvarını GitHub'dan kopyalamak için aşağıdaki komutları çalıştırın:
git clone https://github.com/material-components/material-components-flutter-codelabs.git cd material-components-flutter-codelabs/mdc_100_series git checkout 104-starter_and_103-complete
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. Cihazınızdaki önceki codelab'lerde bulunan Tapınak giriş sayfasını göreceksiniz.
Yapay Zeka | iOS |
4. Arka plan menüsü ekleme
Diğer tüm içeriklerin ve bileşenlerin arkasında bir arka plan görünür. İki katmandan oluşur: bir arka katman (işlemleri ve filtreleri gösteren) ve bir ön katman (içerik gösteren). Gezinme veya içerik filtreleri gibi etkileşimli bilgileri ve işlemleri görüntülemek için bir arka plan kullanabilirsiniz.
Ana ekran uygulama çubuğunu kaldırma
Ana Sayfa widget'ı, ön katmanımızın içeriği olacaktır. Şu anda bir uygulama çubuğu var. Uygulama çubuğunu arka katmana taşıyacağız ve AnaSayfa yalnızca AsimetrikGörünüm'ü içerecek.
home.dart
içinde, build()
işlevini yalnızca AsymmetricView döndürecek şekilde değiştirin:
// TODO: Return an AsymmetricView (104)
return AsymmetricView(products: ProductsRepository.loadProducts(Category.all));
Arka plan widget'ını ekleme
frontLayer
ve backLayer
öğelerini içeren Arka Plan adlı bir widget oluşturun.
backLayer
, listeyi filtrelemek için bir kategori seçmenize olanak tanıyan bir menü (currentCategory
) içerir. Menü seçiminin değişmesini istediğimiz için Arka Plan'ı durum bilgili bir widget haline getireceğiz.
/lib
hedefine backdrop.dart
adlı yeni bir dosya ekleyin:
import 'package:flutter/material.dart';
import 'model/product.dart';
// TODO: Add velocity constant (104)
class Backdrop extends StatefulWidget {
final Category currentCategory;
final Widget frontLayer;
final Widget backLayer;
final Widget frontTitle;
final Widget backTitle;
const Backdrop({
required this.currentCategory,
required this.frontLayer,
required this.backLayer,
required this.frontTitle,
required this.backTitle,
Key? key,
}) : super(key: key);
@override
_BackdropState createState() => _BackdropState();
}
// TODO: Add _FrontLayer class (104)
// TODO: Add _BackdropTitle class (104)
// TODO: Add _BackdropState class (104)
Belirli mülkleri required
ile işaretlediğimizi unutmayın. Bu, oluşturucuda varsayılan değeri olmayan ve null
olamayacak ve dolayısıyla unutulmaması gereken özellikler için en iyi uygulamadır.
Arka plan sınıf tanımının altına _BackdropState sınıfını ekleyin:
// TODO: Add _BackdropState class (104)
class _BackdropState extends State<Backdrop>
with SingleTickerProviderStateMixin {
final GlobalKey _backdropKey = GlobalKey(debugLabel: 'Backdrop');
// TODO: Add AnimationController widget (104)
// TODO: Add BuildContext and BoxConstraints parameters to _buildStack (104)
Widget _buildStack() {
return Stack(
key: _backdropKey,
children: <Widget>[
// TODO: Wrap backLayer in an ExcludeSemantics widget (104)
widget.backLayer,
widget.frontLayer,
],
);
}
@override
Widget build(BuildContext context) {
var appBar = AppBar(
elevation: 0.0,
titleSpacing: 0.0,
// TODO: Replace leading menu icon with IconButton (104)
// TODO: Remove leading property (104)
// TODO: Create title with _BackdropTitle parameter (104)
leading: Icon(Icons.menu),
title: Text('SHRINE'),
actions: <Widget>[
// TODO: Add shortcut to login screen from trailing icons (104)
IconButton(
icon: Icon(
Icons.search,
semanticLabel: 'search',
),
onPressed: () {
// TODO: Add open login (104)
},
),
IconButton(
icon: Icon(
Icons.tune,
semanticLabel: 'filter',
),
onPressed: () {
// TODO: Add open login (104)
},
),
],
);
return Scaffold(
appBar: appBar,
// TODO: Return a LayoutBuilder widget (104)
body: _buildStack(),
);
}
}
build()
işlevi, HomePage'in eskiden olduğu gibi uygulama çubuğu içeren bir İskele döndürür. Ancak, İskele'nin gövdesi bir Yığındır. Bir Grubun alt öğeleri çakışabilir. Her bir alt öğenin boyutu ve konumu, Yığının üst öğesine göre belirtilir.
Şimdi ShrineApp'e bir Backdrop örneği ekleyin.
app.dart
'te backdrop.dart
ve model/product.dart
dosyalarını içe aktarın:
import 'backdrop.dart'; // New code
import 'colors.dart';
import 'home.dart';
import 'login.dart';
import 'model/product.dart'; // New code
import 'supplemental/cut_corners_border.dart';
app.dart,
içinde, frontLayer
değeri HomePage
olan bir Backdrop
döndürerek /
rotasını değiştirin:
// TODO: Change to a Backdrop with a HomePage frontLayer (104)
'/': (BuildContext context) => Backdrop(
// TODO: Make currentCategory field take _currentCategory (104)
currentCategory: Category.all,
// TODO: Pass _currentCategory for frontLayer (104)
frontLayer: HomePage(),
// TODO: Change backLayer field value to CategoryMenuPage (104)
backLayer: Container(color: kShrinePink100),
frontTitle: Text('SHRINE'),
backTitle: Text('MENU'),
),
Projenizi kaydedin. Ana sayfamızın ve uygulama çubuğunun göründüğünü fark edeceksiniz:
Yapay Zeka | iOS |
BackKatman, pembe alanı ön Katman ana sayfasının arkasındaki yeni bir katmanda gösterir.
Grubun gerçekten bir Ana Sayfanın arkasında bir Kapsayıcı olduğunu doğrulamak için Flutter Denetleyicisi'ni kullanabilirsiniz. Bu, aşağıdakine benzer şekilde görünmelidir:
Artık hem katmanların tasarımını hem de içeriğini ayarlayabilirsiniz.
5. Şekil ekleme
Bu adımda, ön katmana stil uygulayarak sol üst köşeye bir kesik ekleyeceksiniz.
Materyal Tasarım, bu özelleştirme türünü şekil olarak ifade eder. Malzeme yüzeylerinin rastgele şekilleri olabilir. Şekiller, yüzeylere vurgu ve stil ekler ve markayı ifade etmek için kullanılabilir. Sıradan dikdörtgen şekiller, kavisli veya açılı köşeler ve kenarlarla ve istenilen sayıda kenarla özelleştirilebilir. Simetrik veya düzensiz olabilirler.
Ön katmana şekil ekleme
Açılı Shrine logosu, Shrine uygulamasının şekil hikayesine ilham verdi. Şekil hikayesi, uygulama genelinde uygulanan şekillerin yaygın bir şekilde kullanılmasıdır. Örneğin, logonun şekli, kendilerine şekil uygulanmış giriş sayfası öğelerinde yansıtılıyor. Bu adımda, ön katmana sol üst köşede açılı bir kesim uygulayacaksınız.
backdrop.dart
alanına _ÖnKatman adlı yeni bir sınıf ekleyin:
// TODO: Add _FrontLayer class (104)
class _FrontLayer extends StatelessWidget {
// TODO: Add on-tap callback (104)
const _FrontLayer({
Key? key,
required this.child,
}) : super(key: key);
final Widget child;
@override
Widget build(BuildContext context) {
return Material(
elevation: 16.0,
shape: const BeveledRectangleBorder(
borderRadius: BorderRadius.only(topLeft: Radius.circular(46.0)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
// TODO: Add a GestureDetector (104)
Expanded(
child: child,
),
],
),
);
}
}
Ardından, _BackdropState'in _buildStack()
işlevinde ön katmanı bir _FrontLayer içine sarın:
Widget _buildStack() {
// TODO: Create a RelativeRectTween Animation (104)
return Stack(
key: _backdropKey,
children: <Widget>[
// TODO: Wrap backLayer in an ExcludeSemantics widget (104)
widget.backLayer,
// TODO: Add a PositionedTransition (104)
// TODO: Wrap front layer in _FrontLayer (104)
_FrontLayer(child: widget.frontLayer),
],
);
}
Yeniden yükleyin.
Yapay Zeka | iOS |
Tapınak'ın birincil yüzeyine özel bir şekil verdik. Ancak bunun uygulama çubuğuyla görsel olarak bağlantı kurmasını istiyoruz.
Uygulama çubuğu rengini değiştirme
app.dart
dosyasında _buildShrineTheme()
işlevini aşağıdaki şekilde değiştirin:
ThemeData _buildShrineTheme() {
final ThemeData base = ThemeData.light(useMaterial3: true);
return base.copyWith(
colorScheme: base.colorScheme.copyWith(
primary: kShrinePink100,
onPrimary: kShrineBrown900,
secondary: kShrineBrown900,
error: kShrineErrorRed,
),
textTheme: _buildShrineTextTheme(base.textTheme),
textSelectionTheme: const TextSelectionThemeData(
selectionColor: kShrinePink100,
),
appBarTheme: const AppBarTheme(
foregroundColor: kShrineBrown900,
backgroundColor: kShrinePink100,
),
inputDecorationTheme: const InputDecorationTheme(
border: CutCornersBorder(),
focusedBorder: CutCornersBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrineBrown900,
),
),
floatingLabelStyle: TextStyle(
color: kShrineBrown900,
),
),
);
}
Çalışır durumda yeniden başlatma. Yeni renkli uygulama çubuğu gösterilir.
Yapay Zeka | iOS |
Bu değişiklik sayesinde kullanıcılar, öndeki beyaz katmanın hemen arkasında bir şey olduğunu görebilir. Kullanıcıların arka planın arka katmanını görebilmesi için hareket ekleyelim.
6. Hareket ekleme
Hareket, uygulamanıza hayat vermenin bir yoludur. Büyük ve etkileyici bir üslup, incelikli ve minimal olabileceği gibi, arada herhangi bir yerde de olabilir. Ancak kullandığınız hareket türünün duruma uygun olması gerektiğini unutmayın. Yinelenen ve düzenli olarak yapılan işlemlere uygulanan hareketler, kullanıcının dikkatini dağıtmaması veya düzenli olarak çok fazla zaman almaması için küçük ve belirsiz olmalıdır. Ancak kullanıcının bir uygulamayı ilk kez açtığı gibi daha dikkat çekici olabilecek uygun durumlar vardır ve bazı animasyonlar kullanıcıya uygulamanızı nasıl kullanacağı konusunda bilgi verebilir.
Menü düğmesine gösterme hareketi ekleme
backdrop.dart
öğesinin üst kısmına, herhangi bir sınıfın veya işlevin kapsamı dışında, animasyonumuzun sahip olmasını istediğimiz hızı temsil edecek bir sabit değer ekleyin:
// TODO: Add velocity constant (104)
const double _kFlingVelocity = 2.0;
_BackdropState için bir AnimationController widget'ı ekleyin, initState()
işlevinde bu widget'ı örneklendirin ve durumun dispose()
işlevinde kaldırın:
// TODO: Add AnimationController widget (104)
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 300),
value: 1.0,
vsync: this,
);
}
// TODO: Add override for didUpdateWidget (104)
@override
void dispose() {
_controller.dispose();
super.dispose();
}
// TODO: Add functions to get and change front layer visibility (104)
AnimationController, animasyonlar için koordinasyon sağlar ve animasyonu oynatma, tersine çevirme ve durdurma API'sini sunar. Şimdi de hareket etmesini sağlayacak işlevlere ihtiyacımız var.
Ön katmanın görünürlüğünü belirleyen ve değiştiren işlevler ekleyin:
// TODO: Add functions to get and change front layer visibility (104)
bool get _frontLayerVisible {
final AnimationStatus status = _controller.status;
return status == AnimationStatus.completed ||
status == AnimationStatus.forward;
}
void _toggleBackdropLayerVisibility() {
_controller.fling(
velocity: _frontLayerVisible ? -_kFlingVelocity : _kFlingVelocity);
}
BackKatman'ı bir ExcludeSemantics widget'ta sarmalayın. Bu widget, arka katman görünür olmadığındabackKatman'ın menü öğelerini anlam ağacından hariç tutar.
return Stack(
key: _backdropKey,
children: <Widget>[
// TODO: Wrap backLayer in an ExcludeSemantics widget (104)
ExcludeSemantics(
child: widget.backLayer,
excluding: _frontLayerVisible,
),
...
_buildStack() işlevini, BuildContext ve BoxConstraints alacak şekilde değiştirin. Ayrıca, RelativeRectTween animasyonu alan bir PositionedTransition ekleyin:
// TODO: Add BuildContext and BoxConstraints parameters to _buildStack (104)
Widget _buildStack(BuildContext context, BoxConstraints constraints) {
const double layerTitleHeight = 48.0;
final Size layerSize = constraints.biggest;
final double layerTop = layerSize.height - layerTitleHeight;
// TODO: Create a RelativeRectTween Animation (104)
Animation<RelativeRect> layerAnimation = RelativeRectTween(
begin: RelativeRect.fromLTRB(
0.0, layerTop, 0.0, layerTop - layerSize.height),
end: const RelativeRect.fromLTRB(0.0, 0.0, 0.0, 0.0),
).animate(_controller.view);
return Stack(
key: _backdropKey,
children: <Widget>[
// TODO: Wrap backLayer in an ExcludeSemantics widget (104)
ExcludeSemantics(
child: widget.backLayer,
excluding: _frontLayerVisible,
),
// TODO: Add a PositionedTransition (104)
PositionedTransition(
rect: layerAnimation,
child: _FrontLayer(
// TODO: Implement onTap property on _BackdropState (104)
child: widget.frontLayer,
),
),
],
);
}
Son olarak, İskele'nin gövdesi için _buildStack işlevini çağırmak yerine, derleyici olarak _buildStack adlı bir LayoutBuilder widget'ı döndürün:
return Scaffold(
appBar: appBar,
// TODO: Return a LayoutBuilder widget (104)
body: LayoutBuilder(builder: _buildStack),
);
Arka planın gerçek toplam yüksekliğini dahil edebilmemiz için ön/arka katman yığınının oluşturulmasını LayoutBuilder'ı kullanarak düzen zamanına kadar erteledik. LayoutBuilder, oluşturucu geri çağırmasının boyut kısıtlamaları sağladığı özel bir widget'tır.
build()
işlevinde, uygulama çubuğundaki ana menü simgesini bir IconButton'a dönüştürün ve düğmeye dokunulduğunda ön katmanın görünürlüğünü değiştirmek için kullanın.
// TODO: Replace leading menu icon with IconButton (104)
leading: IconButton(
icon: const Icon(Icons.menu),
onPressed: _toggleBackdropLayerVisibility,
),
Yeniden yükleyin ve simülatörde menü düğmesine dokunun.
Yapay Zeka | iOS |
Ön katman aşağı doğru hareket eder (kaydırılır). Ancak aşağıya bakarsanız kırmızı bir hata ve bir taşma hatası var. Bunun nedeni, AsymmetricView'un sıkıştırılması ve bu animasyonla küçülmesi nedeniyle Sütunlara daha az yer açılmasıdır. Sonunda sütunlar, verilen alanla kendilerini düzenleyemez ve hata oluşur. Sütunları ListViews ile değiştirirsek, sütun boyutu canlandırıldığı sırada kalmalıdır.
ListView'da ürün sütunlarını sarmalama
supplemental/product_columns.dart
dosyasında, OneProductCardColumn
içindeki sütunu bir ListView ile değiştirin:
class OneProductCardColumn extends StatelessWidget {
const OneProductCardColumn({required this.product, Key? key}) : super(key: key);
final Product product;
@override
Widget build(BuildContext context) {
// TODO: Replace Column with a ListView (104)
return ListView(
physics: const ClampingScrollPhysics(),
reverse: true,
children: <Widget>[
ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 550,
),
child: ProductCard(
product: product,
),
),
const SizedBox(
height: 40.0,
),
],
);
}
}
Sütun MainAxisAlignment.end
içeriyor. Sayfayı alttan başlatmak için reverse: true
simgesini işaretleyin. Alt yayıncının sıralaması, değişikliği telafi etmek için tersine çevrilir.
Yeniden yükleyin ve menü düğmesine dokunun.
Yapay Zeka | iOS |
OneProductCardColumn öğesindeki gri taşma uyarısı kaldırıldı. Şimdi diğerini düzeltelim.
supplemental/product_columns.dart
içinde imageAspectRatio
değerinin hesaplanmasını değiştirin ve TwoProductCardColumn
içindeki sütunu bir ListView ile değiştirin:
// TODO: Change imageAspectRatio calculation (104)
double imageAspectRatio = heightOfImages >= 0.0
? constraints.biggest.width / heightOfImages
: 49.0 / 33.0;
// TODO: Replace Column with a ListView (104)
return ListView(
physics: const ClampingScrollPhysics(),
children: <Widget>[
Padding(
padding: const EdgeInsetsDirectional.only(start: 28.0),
child: top != null
? ProductCard(
imageAspectRatio: imageAspectRatio,
product: top!,
)
: SizedBox(
height: heightOfCards,
),
),
const SizedBox(height: spacerHeight),
Padding(
padding: const EdgeInsetsDirectional.only(end: 28.0),
child: ProductCard(
imageAspectRatio: imageAspectRatio,
product: bottom,
),
),
],
);
Ayrıca imageAspectRatio
içine bazı güvenlik özellikleri de ekledik.
Yeniden yükleyin. Ardından menü düğmesine dokunun.
Yapay Zeka | iOS |
Artık taşma yok.
7. Arka katmana menü ekleme
Menü, metin öğelerine dokunulduğunda dinleyicileri bilgilendiren, dokunulabilir metin öğelerinin bir listesidir. Bu adımda, kategori filtreleme menüsü ekleyeceksiniz.
Menüyü ekleme
Menüyü ön katmana ve etkileşimli düğmeleri arka katmana ekleyin.
lib/category_menu_page.dart
adlı yeni bir dosya oluşturun:
import 'package:flutter/material.dart';
import 'colors.dart';
import 'model/product.dart';
class CategoryMenuPage extends StatelessWidget {
final Category currentCategory;
final ValueChanged<Category> onCategoryTap;
final List<Category> _categories = Category.values;
const CategoryMenuPage({
Key? key,
required this.currentCategory,
required this.onCategoryTap,
}) : super(key: key);
Widget _buildCategory(Category category, BuildContext context) {
final categoryString =
category.toString().replaceAll('Category.', '').toUpperCase();
final ThemeData theme = Theme.of(context);
return GestureDetector(
onTap: () => onCategoryTap(category),
child: category == currentCategory
? Column(
children: <Widget>[
const SizedBox(height: 16.0),
Text(
categoryString,
style: theme.textTheme.bodyLarge,
textAlign: TextAlign.center,
),
const SizedBox(height: 14.0),
Container(
width: 70.0,
height: 2.0,
color: kShrinePink400,
),
],
)
: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Text(
categoryString,
style: theme.textTheme.bodyLarge!.copyWith(
color: kShrineBrown900.withAlpha(153)
),
textAlign: TextAlign.center,
),
),
);
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
padding: const EdgeInsets.only(top: 40.0),
color: kShrinePink100,
child: ListView(
children: _categories
.map((Category c) => _buildCategory(c, context))
.toList()),
),
);
}
}
Alt öğeleri kategori adları olan bir Sütunu sarmalayan bir GestureDetector'dür. Seçilen kategori altı çizili olarak gösterilir.
app.dart
içinde ShrineApp widget'ını durum bilgisizden durum bilgiliye dönüştürün.
- Şunu vurgula:
ShrineApp.
- IDE'nize bağlı olarak kod işlemlerini gösterin:
- Android Studio: ⌥Enter (macOS) veya alt + enter tuşlarına basın
- VS Code: ⌘. (macOS) veya Ctrl+. tuşlarına basın.
- "Convert to StatefulWidget"ı (StatefulWidget'a dönüştür) seçin.
- ShrineAppState sınıfını gizli (_ShrineAppState) olarak değiştirin. ShrineAppState'i sağ tıklayın ve
- Android Studio: Yeniden Düzenleme > Yeniden Adlandır'ı seçin
- VS Kodu: Sembolü Yeniden Adlandır'ı seçin
- Sınıfı gizli hale getirmek için _ShrineAppState değerini girin.
app.dart
içinde, seçili kategori için _ShrineAppState değişkeni ve bu kategoriye dokunulduğunda geri çağırma işlevi ekleyin:
class _ShrineAppState extends State<ShrineApp> {
Category _currentCategory = Category.all;
void _onCategoryTap(Category category) {
setState(() {
_currentCategory = category;
});
}
Sonra, arka katmanı bir CategoryMenüPage olarak değiştirin.
app.dart
ürününde CategoryMenüPage'i içe aktarın:
import 'backdrop.dart';
import 'category_menu_page.dart';
import 'colors.dart';
import 'home.dart';
import 'login.dart';
import 'model/product.dart';
import 'supplemental/cut_corners_border.dart';
build()
işlevinde, örnek değişkenini almak için backKatman alanını CategoryMenüPage ve currentCategory alanını değiştirin.
'/': (BuildContext context) => Backdrop(
// TODO: Make currentCategory field take _currentCategory (104)
currentCategory: _currentCategory,
// TODO: Pass _currentCategory for frontLayer (104)
frontLayer: HomePage(),
// TODO: Change backLayer field value to CategoryMenuPage (104)
backLayer: CategoryMenuPage(
currentCategory: _currentCategory,
onCategoryTap: _onCategoryTap,
),
frontTitle: const Text('SHRINE'),
backTitle: const Text('MENU'),
),
Yeniden yükleyin ve Menü düğmesine dokunun.
Yapay Zeka | iOS |
Bir menü seçeneğine dokunursanız henüz hiçbir şey olmuyor... Gelin bu sorunu çözelim.
home.dart
ürününde Kategori için bir değişken ekleyin ve bunu AsymmetricView'a iletin.
import 'package:flutter/material.dart';
import 'model/product.dart';
import 'model/products_repository.dart';
import 'supplemental/asymmetric_view.dart';
class HomePage extends StatelessWidget {
// TODO: Add a variable for Category (104)
final Category category;
const HomePage({this.category = Category.all, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// TODO: Pass Category variable to AsymmetricView (104)
return AsymmetricView(
products: ProductsRepository.loadProducts(category),
);
}
}
app.dart
içinde frontLayer
için _currentCategory
doğru.
// TODO: Pass _currentCategory for frontLayer (104)
frontLayer: HomePage(category: _currentCategory),
Yeniden yükleyin. Simülatördeki menü düğmesine dokunup bir kategori seçin.
Yapay Zeka | iOS |
Filtrelendi.
Menü seçiminin ardından ön katmanı kapatma
backdrop.dart
içinde, _BackdropState işlevindeki didUpdateWidget()
(widget yapılandırması değiştiğinde çağrılır) işlevi için bir geçersiz kılma ekleyin:
// TODO: Add override for didUpdateWidget() (104)
@override
void didUpdateWidget(Backdrop old) {
super.didUpdateWidget(old);
if (widget.currentCategory != old.currentCategory) {
_toggleBackdropLayerVisibility();
} else if (!_frontLayerVisible) {
_controller.fling(velocity: _kFlingVelocity);
}
}
Çalışır durumda bir yeniden yüklemeyi tetiklemek için projenizi kaydedin. Menü simgesine dokunup bir kategori seçin. Menü otomatik olarak kapanır ve seçilen öğelerin kategorisini görürsünüz. Şimdi bu işlevselliği ön katmana da ekleyeceksiniz.
Ön katmanı aç/kapat
backdrop.dart
içinde, arka plan katmanına dokunma üzerine geri çağırma işlevi ekleyin:
class _FrontLayer extends StatelessWidget {
// TODO: Add on-tap callback (104)
const _FrontLayer({
Key? key,
this.onTap, // New code
required this.child,
}) : super(key: key);
final VoidCallback? onTap; // New code
final Widget child;
Ardından, _FrontLayer'ın çocuğuna bir GestureDetector ekleyin: Sütun'un çocukları:.
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
// TODO: Add a GestureDetector (104)
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: Container(
height: 40.0,
alignment: AlignmentDirectional.centerStart,
),
),
Expanded(
child: child,
),
],
),
Ardından, _buildStack()
işlevindeki _BackdropState özelliğine yeni onTap
mülkünü uygulayın:
PositionedTransition(
rect: layerAnimation,
child: _FrontLayer(
// TODO: Implement onTap property on _BackdropState (104)
onTap: _toggleBackdropLayerVisibility,
child: widget.frontLayer,
),
),
Yeniden yükleyin ve ön katmanın üst kısmına dokunun. Ön katmanın üst kısmına her dokunduğunuzda katman açılıp kapanmalıdır.
8. Markalı simge ekleme
Markalı ikonlar, bilinen simgeler için de geçerlidir. Benzersiz ve markalı bir görünüm için gösterme simgesini özelleştirip başlığımızla birleştirelim.
Menü düğmesi simgesini değiştirme
Yapay Zeka | iOS |
backdrop.dart
uygulamasında yeni bir _BackdropTitle sınıfı oluşturun.
// TODO: Add _BackdropTitle class (104)
class _BackdropTitle extends AnimatedWidget {
final void Function() onPress;
final Widget frontTitle;
final Widget backTitle;
const _BackdropTitle({
Key? key,
required Animation<double> listenable,
required this.onPress,
required this.frontTitle,
required this.backTitle,
}) : _listenable = listenable,
super(key: key, listenable: listenable);
final Animation<double> _listenable;
@override
Widget build(BuildContext context) {
final Animation<double> animation = _listenable;
return DefaultTextStyle(
style: Theme.of(context).textTheme.titleLarge!,
softWrap: false,
overflow: TextOverflow.ellipsis,
child: Row(children: <Widget>[
// branded icon
SizedBox(
width: 72.0,
child: IconButton(
padding: const EdgeInsets.only(right: 8.0),
onPressed: this.onPress,
icon: Stack(children: <Widget>[
Opacity(
opacity: animation.value,
child: const ImageIcon(AssetImage('assets/slanted_menu.png')),
),
FractionalTranslation(
translation: Tween<Offset>(
begin: Offset.zero,
end: const Offset(1.0, 0.0),
).evaluate(animation),
child: const ImageIcon(AssetImage('assets/diamond.png')),
)]),
),
),
// Here, we do a custom cross fade between backTitle and frontTitle.
// This makes a smooth animation between the two texts.
Stack(
children: <Widget>[
Opacity(
opacity: CurvedAnimation(
parent: ReverseAnimation(animation),
curve: const Interval(0.5, 1.0),
).value,
child: FractionalTranslation(
translation: Tween<Offset>(
begin: Offset.zero,
end: const Offset(0.5, 0.0),
).evaluate(animation),
child: backTitle,
),
),
Opacity(
opacity: CurvedAnimation(
parent: animation,
curve: const Interval(0.5, 1.0),
).value,
child: FractionalTranslation(
translation: Tween<Offset>(
begin: const Offset(-0.25, 0.0),
end: Offset.zero,
).evaluate(animation),
child: frontTitle,
),
),
],
)
]),
);
}
}
_BackdropTitle
, AppBar
widget'ının title
parametresi için düz Text
widget'ının yerini alacak özel bir widget'tır. Animasyonlu bir menü simgesine ve ön ve arka başlıklar arasında animasyonlu geçişlere sahip. Animasyonlu menü simgesi yeni bir öğe kullanacak. Yeni slanted_menu.png
referansı, pubspec.yaml
öğesine eklenmelidir.
assets:
- assets/diamond.png
# TODO: Add slanted menu asset (104)
- assets/slanted_menu.png
- packages/shrine_images/0-0.jpg
AppBar
oluşturucuda leading
özelliğini kaldırın. Özel markalı simgesinin orijinal leading
widget'ının yerine oluşturulması için kaldırma işleminin yapılması gerekir. Marka simgesi için listenable
animasyonu ve onPress
işleyicisi _BackdropTitle
'ye iletilir. frontTitle
ve backTitle
, arka plan başlığında oluşturulabilmeleri için de iletilir. AppBar
öğesinin title
parametresi şöyle görünmelidir:
// TODO: Create title with _BackdropTitle parameter (104)
title: _BackdropTitle(
listenable: _controller.view,
onPress: _toggleBackdropLayerVisibility,
frontTitle: widget.frontTitle,
backTitle: widget.backTitle,
),
Markalı simge, _BackdropTitle.
içinde oluşturulur. Stack
animasyonlu simge içerir: Eğik bir menü ve basılması için IconButton
içine yerleştirilmiş bir elmas. Daha sonra IconButton
, yatay simge hareketine yer açmak için bir SizedBox
içine sarmalanır.
Flutter'ın "her şey bir widget" mimarisi, tamamen yeni bir özel AppBar
widget oluşturmak zorunda kalmadan varsayılan AppBar
'ın düzeninin değiştirilmesine olanak tanır. Başlangıçta Text
widget'ı olan title
parametresi, daha karmaşık bir _BackdropTitle
ile değiştirilebilir. _BackdropTitle
, özel simgeyi de içerdiğinden, leading
özelliğinin yerini alır. Bu özellik artık atlanabilir. Bu basit widget değişimi, işlem simgeleri gibi diğer parametrelerin hiçbiri değiştirilmeden gerçekleştirilir. İşlem simgeleri kendi kendilerine çalışmaya devam eder.
Giriş ekranına geri dönen bir kısayol ekleme
backdrop.dart,
ürününde, uygulama çubuğunun sonundaki iki simgeden giriş ekranına kısayol ekleyin: Simgelerin anlamsal etiketlerini yeni amaçlarını yansıtacak şekilde değiştirin.
// TODO: Add shortcut to login screen from trailing icons (104)
IconButton(
icon: const Icon(
Icons.search,
semanticLabel: 'login', // New code
),
onPressed: () {
// TODO: Add open login (104)
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => LoginPage()),
);
},
),
IconButton(
icon: const Icon(
Icons.tune,
semanticLabel: 'login', // New code
),
onPressed: () {
// TODO: Add open login (104)
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => LoginPage()),
);
},
),
Yeniden yüklemeyi denerseniz hata alırsınız. Hatayı düzeltmek için login.dart
dosyasını içe aktarın:
import 'login.dart';
Uygulamayı yeniden yükleyin ve giriş ekranına dönmek için arama veya ayar düğmelerine dokunun.
9. Tebrikler!
Bu dört codelab süresince marka kişiliğini ve stilini yansıtan benzersiz, şık kullanıcı deneyimleri oluşturmak için Materyal Bileşenleri nasıl kullanacağınızı öğrendiniz.
Sonraki adımlar
Bu codelab'de (MDC-104) kod laboratuvarları dizisi tamamlanır. Malzeme Bileşenleri widget kataloğunu ziyaret ederek Material Flutter'da daha da fazla bileşeni keşfedebilirsiniz.
Daha iddialı bir hedef için markalı simgeyi, arka plan görünür hale getirildiğinde iki simge arasında animasyonlu geçiş yapan bir AnimatedIcon ile değiştirmeyi deneyin.
İlgi alanlarınıza göre denemeniz için birçok başka Flutter codelab de mevcuttur. İlginizi çekebilecek başka bir Materyal'e özel codelab'imiz de var: Flutter için Materyal Hareket ile Güzel Geçişler Oluşturma.