رفرفة: ضخ ما يصل AppBar و SliverAppBar

يستخدم 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 ، وبالتالي اكتشاف كيف يمكننا تطبيق الأدوات المصغّرة الخاصة بنا.

Source: https://habr.com/ru/post/ar456054/


All Articles