يستخدم Flutter AppBar المعروف لإنشاء شريط أدوات ، ولكن عندما نحتاج إلى شريط أدوات ديناميكي يعرض المحتوى عند التمرير ، فإننا نستخدم أداة SliverAppBar الممتازة.
تسمح لك كلتا الحاجتين بجعل التطبيق أكثر جمالا ، والذي في Flutter ، بلا شك ، بسيط للغاية.
لقد رأيت الكثير من الأسئلة على StackOverflow وفي مجموعات Facebook حول كيفية تغيير AppBar و SliverAppBar من حيث السلوك أو التصميم.
دعونا ننظر في مهمتين.
المهمة 1
نريد إنشاء AppBar غير مشدود إلى أعلى الشاشة ، ولكن ليس كما نفعل عادةً. نريد إضافة درج (قائمة جانبية) ، سيتم فتح AppBar منها عند الافتتاح. هذا كل شيء: AppBar الخاصة بنا مع الأبعاد التي نحتاجها.
المشكلة هي ، كما نعلم ، أن AppBar له حجم افتراضي ، ولا يمكننا تغييره. بعد الاطلاع على الكود المصدري ، نرى المعلمة appBar في Scaffold ، ونرى أنه يقبل عنصر واجهة مستخدم من نوع PreferredSizeWidget ، والآن ننظر إلى شفرة مصدر AppBar ونكتشف أن هذا هو StatefulWidget فقط الذي يقوم بتنفيذ PreferredSizeWidget .
النقطة صغيرة: فقط قم بإنشاء عنصر واجهة المستخدم الخاص بنا الذي يقوم بتنفيذ PreferredSizeWidget .
هذا ما نريده
كيف يمكنك أن تفعل ذلك عندما تنقر على زر القائمة من AppBar لدينا ، يتم فتح قائمة جانبية.
يمكننا القيام بذلك بطريقتين:
باستخدام "AppBar"
هكذا يمكن لـ AppBar التحكم في فتح الشريط الجانبي داخل سقالة .
class Sample1 extends StatelessWidget { @override Widget build(BuildContext context) { return SafeArea( child: Scaffold( drawer: Drawer(), appBar: MyCustomAppBar( height: 150, ), body: Center( child: FlutterLogo( size: MediaQuery.of(context).size.width / 2, ), ), ), ); } } class MyCustomAppBar extends StatelessWidget implements PreferredSizeWidget { final double height; const MyCustomAppBar({ Key key, @required this.height, }) : super(key: key); @override Widget build(BuildContext context) { return Column( children: [ Container( color: Colors.grey[300], child: Padding( padding: EdgeInsets.all(30), child: AppBar( title: Container( color: Colors.white, child: TextField( decoration: InputDecoration( hintText: "Search", contentPadding: EdgeInsets.all(10), ), ), ), actions: [ IconButton( icon: Icon(Icons.verified_user), onPressed: () => null, ), ], ) , ), ), ], ); } @override Size get preferredSize => Size.fromHeight(height); }
باستخدام عنصر واجهة مستخدم مخصص
لدينا هنا المزيد من المرونة ويمكنك استخدام GlobalKey مثل ScaffoldState أو InheritedWidget من Scaffold ، وبالتالي نتمكن من الوصول إلى أساليب الحالة لفتح الدرج .
import 'package:flutter/material.dart'; class Sample1 extends StatelessWidget { @override Widget build(BuildContext context) { return SafeArea( child: Scaffold( drawer: Drawer(), appBar: MyCustomAppBar( height: 150, ), body: Center( child: FlutterLogo( size: MediaQuery.of(context).size.width / 2, ), ), ), ); } } class MyCustomAppBar extends StatelessWidget implements PreferredSizeWidget { final double height; const MyCustomAppBar({ Key key, @required this.height, }) : super(key: key); @override Widget build(BuildContext context) { return Column( children: [ Container( color: Colors.grey[300], child: Padding( padding: EdgeInsets.all(30), child: Container( color: Colors.red, padding: EdgeInsets.all(5), child: Row(children: [ IconButton( icon: Icon(Icons.menu), onPressed: () { Scaffold.of(context).openDrawer(); }, ), Expanded( child: Container( color: Colors.white, child: TextField( decoration: InputDecoration( hintText: "Search", contentPadding: EdgeInsets.all(10), ), ), ), ), IconButton( icon: Icon(Icons.verified_user), onPressed: () => null, ), ]), ), ), ), ], ); } @override Size get preferredSize => Size.fromHeight(height); }
يؤدي
بسيط ، أليس كذلك؟ دعونا ننظر في المهمة الثانية ل SliverAppBar .
المهمة 2
كما نعلم ، يعمل SliverAppBar كما يلي:
ما نريده هو وضع البطاقة في SliverAppBar لدينا ، كما هو موضح في الشكل التالي.
انتظر ، ولكن المحتوى الموجود داخل SliverAppBar يتم اقتصاصه ، لذلك لا يمكن أن يتجاوز ما يجب فعله؟
بدون ذعر ، دعنا نرى الكود المصدري لـ SliverAppBar ، ويا للدهشة ، هذه هي StatefulWidget باستخدام SliverPersistentHeader من الداخل ، وهذا هو السر الكامل.
سنقوم بإنشاء SliverPersistentHeaderDelegate الخاص بنا لاستخدام SliverPersistentHeader .
class Sample2 extends StatelessWidget { @override Widget build(BuildContext context) { return SafeArea( child: Material( child: CustomScrollView( slivers: [ SliverPersistentHeader( delegate: MySliverAppBar(expandedHeight: 200), pinned: true, ), SliverList( delegate: SliverChildBuilderDelegate( (_, index) => ListTile( title: Text("Index: $index"), ), ), ) ], ), ), ); } } class MySliverAppBar extends SliverPersistentHeaderDelegate { final double expandedHeight; MySliverAppBar({@required this.expandedHeight}); @override Widget build( BuildContext context, double shrinkOffset, bool overlapsContent) { return Stack( fit: StackFit.expand, overflow: Overflow.visible, children: [ Image.network( "https://images.pexels.com/photos/396547/pexels-photo-396547.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500", fit: BoxFit.cover, ), Center( child: Opacity( opacity: shrinkOffset / expandedHeight, child: Text( "MySliverAppBar", style: TextStyle( color: Colors.white, fontWeight: FontWeight.w700, fontSize: 23, ), ), ), ), Positioned( top: expandedHeight / 2 - shrinkOffset, left: MediaQuery.of(context).size.width / 4, child: Opacity( opacity: (1 - shrinkOffset / expandedHeight), child: Card( elevation: 10, child: SizedBox( height: expandedHeight, width: MediaQuery.of(context).size.width / 2, child: FlutterLogo(), ), ), ), ), ], ); } @override double get maxExtent => expandedHeight; @override double get minExtent => kToolbarHeight; @override bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true; }
يؤدي
فعلت ، يتم حل كل المهام.
الأمثلة موجودة في هذا المستودع .
استنتاج
غالبًا ما نشعر بالإحباط عندما لا نعثر على أي خصائص لعنصر واجهة المستخدم ، لكنك تحتاج فقط إلى إلقاء نظرة على الكود المصدر الخاص بها لفهم كيفية تنفيذها في Flutter ، وبالتالي اكتشاف كيف يمكننا تطبيق الأدوات المصغّرة الخاصة بنا.