SliverPersistentHeaderDelegate не полностью свернут - PullRequest
2 голосов
/ 05 мая 2020

Мне сложно создать настраиваемую сворачивающуюся панель инструментов, ниже прилагается видео для обычного случая.

no problem but not snappy

Then here's a screen record of the misbehavior, most of the time this happens.

не рухнул

Помимо того, что прокрутка не такая быстрая, во втором видео вы увидите, что полоска наверху не полностью свернута.

У вас есть какие-нибудь предложение по повышению производительности приложения и решение проблемы?

вот мой код внутри SliverPersistentHeaderDelegate

class DashboardHeaderPersistentDelegate extends SliverPersistentHeaderDelegate {

...

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {

    double shrinkPercentage = min(1, shrinkOffset / (maxExtent - minExtent));
    double titleTopMargin = titleCollapsedTopPadding +
        (titleExpandedTopPadding - titleCollapsedTopPadding) *
            (1 - shrinkPercentage);
    double titleFontSize = titleCollapsedFontSize +
        (titleExpandedFontSize - titleCollapsedFontSize) *
            (1 - shrinkPercentage);
    double infoWidgetHeight = minExtent +
        (maxExtent - minExtent) -
        shrinkOffset -
        titleTopMargin -
        titleFontSize -
        44;
    double collapasedInfoOpacity = max(0, shrinkPercentage-.7)/.3;

    return Material(
      elevation: 0,
      shadowColor: Colors.white,
      child: SafeArea(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            Container(
              height: titleFontSize,
              alignment: Alignment.center,
              child: Text(
                '\$ 5329.05',
                style: TextStyle(
                    fontFamily: 'Barlow',
                    fontSize: titleFontSize,
                    fontWeight: FontWeight.w500),
              ),
              margin: EdgeInsets.only(top: titleTopMargin, bottom: 8),
            ),
            Container(
              height: shrinkPercentage == 1 ? 20 : infoWidgetHeight,
              width: MediaQuery.of(context).size.width,
              alignment: Alignment.center,
              child: Stack(
                alignment: Alignment.bottomCenter,
                children: [
                  Opacity(
                    opacity: 1 - shrinkPercentage,
                    child: _buildInformationWidget(context),
                  ),
                  Opacity(
                    opacity: collapasedInfoOpacity,
                    child: Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      child: _buildCollapsedInformationWidget(),
                    ),
                  )
                ],
              ),
            )
          ],
        ),
      ),
    );
  }

  Widget _buildInformationWidget(BuildContext context) => ClipRect(
        child: OverflowBox(
          maxWidth: double.infinity,
          maxHeight: double.infinity,
          child: FittedBox(
            fit: BoxFit.fitWidth,
            alignment: Alignment.center,
            child: Container(
              width: MediaQuery.of(context).size.width,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                  Text(
                    'AVAILABLE BALANCE',
                    style: TextStyle(
                        fontSize: 12,
                        fontWeight: FontWeight.w900,
                        color: Colors.black26),
                  ),
                  Padding(
                    padding: const EdgeInsets.only(top: 16),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      crossAxisAlignment: CrossAxisAlignment.center,
                      children: <Widget>[
                        Container(
                          width: 100,
                          child: Text(
                            '\$ 11200',
                            textAlign: TextAlign.right,
                            style: TextStyle(
                                fontFamily: 'Barlow',
                                fontSize: 18,
                                fontWeight: FontWeight.w700,
                                color: Colors.green[400]),
                          ),
                        ),
                        Text(
                          ' I ',
                          style: TextStyle(fontSize: 20, color: Colors.black12),
                        ),
                        Container(
                          width: 100,
                          child: Text(
                            '\$ 400',
                            style: TextStyle(
                                fontFamily: 'Barlow',
                                fontSize: 18,
                                fontWeight: FontWeight.w700,
                                color: Colors.red[400]),
                          ),
                        )
                      ],
                    ),
                  ),
                  Container(
                    margin: EdgeInsets.only(left: 12, top: 12),
                    alignment: Alignment.centerLeft,
                    child: Text(
                      "CATEGORIES",
                      style: TextStyle(
                          fontSize: 12,
                          fontWeight: FontWeight.w900,
                          color: Colors.black26),
                    ),
                  ),
                  Container(
                    height: 88,
                    child: ListView.builder(
                        scrollDirection: Axis.horizontal,
                        itemCount: categories.length,
                        itemBuilder: (context, index) {
                          return Padding(
                            padding: EdgeInsets.only(
                                left: (index == 0) ? 24.0 : 8.0,
                                right: (index == categories.length - 1)
                                    ? 24.0
                                    : 8.0),
                            child: _buildCategoryItem(
                                categoriesIcons[index], categories[index], .9),
                          );
                        }),
                  )
                ],
              ),
            ),
          ),
        ),
      );

  Widget _buildCollapsedInformationWidget() => Row(
        children: [
          Text("Recent"),
          Spacer(),
          Container(
            child: Text(
              '\$ 11200',
              textAlign: TextAlign.right,
              style: TextStyle(
                  fontFamily: 'Barlow',
                  fontSize: 14,
                  fontWeight: FontWeight.w700,
                  color: Colors.green[400]),
            ),
          ),
          Text(
            ' I ',
            style: TextStyle(fontSize: 20, color: Colors.black12),
          ),
          Container(
            child: Text(
              '\$ 400',
              style: TextStyle(
                  fontFamily: 'Barlow',
                  fontSize: 14,
                  fontWeight: FontWeight.w700,
                  color: Colors.red[400]),
            ),
          )
        ],
      );

  Widget _buildCategoryItem(
          IconData data, String categoryTitle, double percentage) =>
      Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Stack(
            alignment: Alignment.center,
            children: <Widget>[
              Container(
                decoration: BoxDecoration(
                    border: Border.all(width: 1, color: Colors.black12),
                    borderRadius: BorderRadius.circular(28),
                    color: Colors.blue[400]),
                child: Padding(
                  padding: const EdgeInsets.all(10.0),
                  child: Icon(
                    data,
                    size: 28,
                    color: Colors.white,
                  ),
                ),
              ),
              Container(
                width: 40,
                height: 40,
                child: CircularProgressIndicator(
                  value: percentage,
                  strokeWidth: 2,
                  valueColor: AlwaysStoppedAnimation(Colors.white),
                ),
              )
            ],
          ),
          Container(
            width: 72,
            alignment: Alignment.center,
            child: Text(categoryTitle,
                overflow: TextOverflow.ellipsis,
                textAlign: TextAlign.center,
                maxLines: 1,
                style: TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.w400,
                    color: Colors.black45)),
          )
        ],
      );

...

}

1 Ответ

1 голос
/ 11 августа 2020

Когда это возможно .. Используйте виджеты зависимости от родительского размера, вместо ручного вычисления дочерних размеров.

  1. FittedBox . Можно масштабировать размер шрифта дочернего виджета в соответствии с размером родительского элемента и параметром подгонки.
  2. ConstrainedBox , BoxConstraints.tightFor
  3. Expanded . Вы, наверное, знаете, как это работает.

введите описание изображения здесь

    import 'dart:math';

    import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';

    void main() {
      runApp(MyApp());
    }

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: Scaffold(
            body: SafeArea(
              child: MyHomePage(),
            ),
          ),
        );
      }
    }

    class MyHomePage extends StatefulWidget {
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }

    class _MyHomePageState extends State<MyHomePage> {
      @override
      Widget build(BuildContext context) {
        return CustomScrollView(
          slivers: [
            SliverPersistentHeader(
              pinned: true,
              floating: false,
              delegate: DashboardHeaderPersistentDelegate(),
            ),
            SliverList(
              delegate: SliverChildBuilderDelegate(
                  (_, i) => Card(
                        margin: EdgeInsets.all(10),
                        child: Padding(
                            padding: const EdgeInsets.all(8.0),
                            child: Row(
                              mainAxisAlignment: MainAxisAlignment.spaceAround,
                              children: [
                                Expanded(
                                  flex: 1,
                                  child: Text(i.toString()),
                                ),
                                Expanded(
                                  flex: 3,
                                  child: Text('Text'),
                                ),
                              ],
                            )),
                      ),
                  childCount: 100),
            ),
          ],
        );
      }
    }

    const categories = [
      'Grocieries',
      'Transport',
      'House Rent',
      'Shopping',
      'Career'
    ];
    const categoriesIcons = [
      Icons.ac_unit,
      Icons.access_alarms,
      Icons.dashboard,
      Icons.accessible_forward,
      Icons.backspace,
    ];

    class DashboardHeaderPersistentDelegate extends SliverPersistentHeaderDelegate {
      @override
      Widget build(
          BuildContext context, double shrinkOffset, bool overlapsContent) {
        double shrinkPercentage = min(1, shrinkOffset / (maxExtent - minExtent));

        return Container(
          decoration: BoxDecoration(
            border: Border.all(
              color: Colors.blueAccent,
            ),
          ),
          child: Material(
            elevation: 0,
            shadowColor: Colors.white,
            child: SafeArea(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: <Widget>[
                  ConstrainedBox(
                    constraints: BoxConstraints.tightFor(
                      height: max(60, 100 * (1 - shrinkPercentage)),
                    ),
                    child: FittedBox(
                      child: Container(
                        padding: EdgeInsets.all(20),
                        width: 200,
                        child: Text(
                          '\$ 5329.05',
                          style: TextStyle(
                            fontFamily: 'Barlow',
                            fontSize: 30,
                            fontWeight: FontWeight.w500,
                          ),
                        ),
                      ),
                    ),
                  ),
                  Expanded(
                    child: Stack(
                      alignment: Alignment.bottomCenter,
                      children: [
                        if (shrinkPercentage != 1)
                          Opacity(
                            opacity: 1 - shrinkPercentage,
                            child: _buildInformationWidget(context),
                          ),
                        if (shrinkPercentage != 0)
                          Opacity(
                            opacity: shrinkPercentage,
                            child: Padding(
                              padding: const EdgeInsets.symmetric(horizontal: 8),
                              child: _buildCollapsedInformationWidget(),
                            ),
                          )
                      ],
                    ),
                  )
                ],
              ),
            ),
          ),
        );
      }

      Widget _buildInformationWidget(BuildContext context) => ClipRect(
            child: OverflowBox(
              maxWidth: double.infinity,
              maxHeight: double.infinity,
              child: FittedBox(
                fit: BoxFit.fitWidth,
                alignment: Alignment.center,
                child: Container(
                  width: MediaQuery.of(context).size.width,
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: <Widget>[
                      Text(
                        'AVAILABLE BALANCE',
                        style: TextStyle(
                            fontSize: 12,
                            fontWeight: FontWeight.w900,
                            color: Colors.black26),
                      ),
                      Padding(
                        padding: const EdgeInsets.only(top: 16),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          crossAxisAlignment: CrossAxisAlignment.center,
                          children: <Widget>[
                            Container(
                              width: 100,
                              child: Text(
                                '\$ 11200',
                                textAlign: TextAlign.right,
                                style: TextStyle(
                                    fontFamily: 'Barlow',
                                    fontSize: 18,
                                    fontWeight: FontWeight.w700,
                                    color: Colors.green[400]),
                              ),
                            ),
                            Text(
                              ' I ',
                              style: TextStyle(fontSize: 20, color: Colors.black12),
                            ),
                            Container(
                              width: 100,
                              child: Text(
                                '\$ 400',
                                style: TextStyle(
                                    fontFamily: 'Barlow',
                                    fontSize: 18,
                                    fontWeight: FontWeight.w700,
                                    color: Colors.red[400]),
                              ),
                            )
                          ],
                        ),
                      ),
                      Container(
                        margin: EdgeInsets.only(left: 12, top: 12),
                        alignment: Alignment.centerLeft,
                        child: Text(
                          "CATEGORIES",
                          style: TextStyle(
                              fontSize: 12,
                              fontWeight: FontWeight.w900,
                              color: Colors.black26),
                        ),
                      ),
                      Container(
                        height: 88,
                        child: ListView.builder(
                            scrollDirection: Axis.horizontal,
                            itemCount: categories.length,
                            itemBuilder: (context, index) {
                              return Padding(
                                padding: EdgeInsets.only(
                                    left: (index == 0) ? 24.0 : 8.0,
                                    right: (index == categories.length - 1)
                                        ? 24.0
                                        : 8.0),
                                child: _buildCategoryItem(
                                    categoriesIcons[index], categories[index], .9),
                              );
                            }),
                      )
                    ],
                  ),
                ),
              ),
            ),
          );

      Widget _buildCollapsedInformationWidget() => Row(
            children: [
              Text("Recent"),
              Spacer(),
              Container(
                child: Text(
                  '\$ 11200',
                  textAlign: TextAlign.right,
                  style: TextStyle(
                      fontFamily: 'Barlow',
                      fontSize: 14,
                      fontWeight: FontWeight.w700,
                      color: Colors.green[400]),
                ),
              ),
              Text(
                ' I ',
                style: TextStyle(fontSize: 20, color: Colors.black12),
              ),
              Container(
                child: Text(
                  '\$ 400',
                  style: TextStyle(
                      fontFamily: 'Barlow',
                      fontSize: 14,
                      fontWeight: FontWeight.w700,
                      color: Colors.red[400]),
                ),
              )
            ],
          );

      Widget _buildCategoryItem(
              IconData data, String categoryTitle, double percentage) =>
          Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Stack(
                alignment: Alignment.center,
                children: <Widget>[
                  Container(
                    decoration: BoxDecoration(
                        border: Border.all(width: 1, color: Colors.black12),
                        borderRadius: BorderRadius.circular(28),
                        color: Colors.blue[400]),
                    child: Padding(
                      padding: const EdgeInsets.all(10.0),
                      child: Icon(
                        data,
                        size: 28,
                        color: Colors.white,
                      ),
                    ),
                  ),
                  Container(
                    width: 40,
                    height: 40,
                    child: CircularProgressIndicator(
                      value: percentage,
                      strokeWidth: 2,
                      valueColor: AlwaysStoppedAnimation(Colors.white),
                    ),
                  )
                ],
              ),
              Container(
                width: 72,
                alignment: Alignment.center,
                child: Text(categoryTitle,
                    overflow: TextOverflow.ellipsis,
                    textAlign: TextAlign.center,
                    maxLines: 1,
                    style: TextStyle(
                        fontSize: 14,
                        fontWeight: FontWeight.w400,
                        color: Colors.black45)),
              )
            ],
          );

      @override
      double get maxExtent => 300;

      @override
      double get minExtent => 80;

      @override
      bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
    }
...