Skip to content

Tabbar.image does not render at initialIndex for the first time #59143

Closed

Description

@mzl1988

✓] Flutter (Channel stable, v1.17.3, on Mac OS X 10.15.5 19F101, locale zh-Hans-CN)

[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.3)
[✓] Xcode - develop for iOS and macOS (Xcode 11.5)
[✓] Android Studio (version 4.0)
[✓] VS Code (version 1.45.1)
[✓] Connected device (1 available)
未命名

TabBar(
                    labelColor: Theme.of(context).accentColor,
                    unselectedLabelColor: Theme.of(context).hintColor,
                    indicatorSize: TabBarIndicatorSize.label,
                    indicator: BoxDecoration(
                      image: DecorationImage(
                        image: AssetImage('assets/618.png'),
                        fit: BoxFit.cover,
                      ),
                    ),
                    isScrollable: true,
                    tabs: _tabs.map((String name) => Tab(text: name)).toList(),
                  ),

Activity

  1. iapicca commented on Jun 10, 2020

    @iapicca

    Hi @mzl1988
    it is possible the the widget containing the TabBar is build
    before the image is loaded
    could you please provide a minimal complete reproducible code sample.
    Thank you

  2. added
    in triagePresently being triaged by the triage team
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Jun 10, 2020
  3. added
    p: material_uimaterial_ui package in flutter/packages
    frameworkflutter/packages/flutter repository. See also f: labels.
    on Jun 10, 2020
  4. mzl1988 commented on Jun 12, 2020

    @mzl1988
    Author
  5. removed
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Jun 12, 2020
  6. removed
    in triagePresently being triaged by the triage team
    on Jun 12, 2020
  7. iapicca commented on Jun 12, 2020

    @iapicca

    Hi @mzl1988
    I've tested this modified version of your code sample with the latest master

    code
    import 'dart:async';
    import 'dart:ui' as ui;
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      static const Widget _home = MyHomePage();
      @override
      Widget build(BuildContext context) => MaterialApp(
            home: _home,
          );
    }
    
    class MyHomePage extends StatelessWidget {
      const MyHomePage();
      static const String _url =
          'https://flutter.dev/assets/flutter-lockup-c13da9c9303e26b8d5fc208d2a1fa20c1ef47eb021ecadf27046dea04c0cebf6.png';
    
      Future<ImageProvider> _provider() async {
        NetworkImageDecoder _decoder = NetworkImageDecoder(
          image: NetworkImage(_url),
        );
        MemoryImageProvider _provider = MemoryImageProvider(image: await _decoder());
        return _provider();
      }
    
      @override
      Widget build(BuildContext context) => FutureBuilder<ImageProvider>(
          future: _provider(),
          builder: (BuildContext context, AsyncSnapshot<ImageProvider> snapshot) =>
              snapshot.hasError
                  ? const Material(
                      child: Center(
                        child: Text('error'),
                      ),
                    )
                  : !snapshot.hasData
                      ? const Material(
                          child: Center(
                            child: Text('waiting'),
                          ),
                        )
                      : TabbarPage(
                          image: snapshot.data,
                        ),
                      );
    }
    
    class TabbarPage extends StatelessWidget {
      const TabbarPage({
        @required this.image,
        key,
      }) : super(key: key);
      final ImageProvider image;
    
      static const List<String> _tabs = ['One', 'Two', 'Three'];
    
      @override
      Widget build(BuildContext context) {
        return DefaultTabController(
          length: _tabs.length,
          child: Scaffold(
            appBar: AppBar(
              title: const Text('TabBar Sample'),
              bottom: TabBar(
                indicator: BoxDecoration(
                  image: DecorationImage(
                    image: image,
                    fit: BoxFit.cover,
                  ),
                ),
                isScrollable: true,
                tabs: [for (String name in _tabs) Tab(text: name)],
              ),
            ),
            body: TabBarView(children: [
              for (String name in _tabs)
                Center(
                  child: Text('page $name'),
                ),
            ]),
          ),
        );
      }
    }
    
    class NetworkImageDecoder {
      const NetworkImageDecoder({this.image});
      final NetworkImage image;
    
      Future<ui.Image> call() async {
        final Completer<ImageInfo> completer = Completer();
        image.resolve(ImageConfiguration()).addListener(
              ImageStreamListener(
                (ImageInfo info, bool _) => completer.complete(info),
              ),
            );
        final ImageInfo _info = await completer.future;
        return _info.image;
      }
    }
    
    class MemoryImageProvider {
      const MemoryImageProvider({this.image});
      final ui.Image image;
    
      Future<ImageProvider> call() async {
        final ByteData _data =
            await image.toByteData(format: ui.ImageByteFormat.png);
        return MemoryImage(_data.buffer.asUint8List());
      }
    }
    
    doctor
    [✓] Flutter (Channel master, 1.20.0-1.0.pre.29, on Mac OS X 10.15.5 19F101, locale en-GB)
        • Flutter version 1.20.0-1.0.pre.29 at /Users/nevercode/development/flutter_master
        • Framework revision 1d407de925 (11 hours ago), 2020-06-11 19:45:02 -0400
        • Engine revision d417772d7a
        • Dart version 2.9.0 (build 2.9.0-15.0.dev f043f9e5f6)
    
    
    [✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
        • Android SDK at /Users/nevercode/Library/Android/sdk
        • Platform android-29, build-tools 29.0.2
        • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
        • Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)
        • All Android licenses accepted.
    
    [✓] Xcode - develop for iOS and macOS (Xcode 11.5)
        • Xcode at /Applications/Xcode.app/Contents/Developer
        • Xcode 11.5, Build version 11E608c
        • CocoaPods version 1.9.0
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] Android Studio (version 3.6)
        • Android Studio at /Applications/Android Studio.app/Contents
        • Flutter plugin version 45.1.1
        • Dart plugin version 192.7761
        • Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)
    
    [✓] VS Code
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.11.0
    
    [✓] Connected device (3 available)
        • Pixel 3a   • 965AY0WP5C • android-arm64  • Android 10 (API 29)
        • Web Server • web-server • web-javascript • Flutter Tools
        • Chrome     • chrome     • web-javascript • Google Chrome 83.0.4103.97
    
    • No issues found!

    I can reproduce the issue
    Thank you

  8. added
    a: assetsPackaging, accessing, or using assets
    a: imagesLoading, displaying, rendering images
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    on Jun 12, 2020
  9. OctMon commented on Aug 6, 2020

    @OctMon

    Try this.

    tabs: _tabs.map((String name) => Tab(
    //          text: name,
              icon: Stack(
                alignment: Alignment.center,
                children: <Widget>[
                  Text(name),
                  Align(
                     alignment: Alignment.bottomCenter,
                      child: Offstage(
                        child: Image.asset(
                          "assets/618.png",
                        ),
                      )),
                ],
              ),
            )).toList(),
            ),
    
  10. Nomeleel commented on Nov 14, 2020

    @Nomeleel
  11. 30 remaining items

  12. Mahm0ud-Ahmed commented on Aug 2, 2024

    @Mahm0ud-Ahmed
  13. Cedar-byte commented on Nov 4, 2024

    @Cedar-byte

    precacheImage(AssetImage("xxx.png"), context); can be solved.

    Works for me. Thanks.

  14. added
    P2Important issues not at the top of the work list
    workaround availableThere is a workaround available to overcome the issue
    on Mar 4, 2025
  15. jingkeling commented on Dec 2, 2025

    @jingkeling
  16. self-assigned this
    on Dec 8, 2025
  17. huycozy commented on Dec 8, 2025

    @huycozy
    Member

    I'm looking at this issue and found why this happens:

    TabBar is using _indicatorPainter, which is a CustomPainter to manage painting things in the whole bar, and each image on tabs is painted by a BoxPainter named _painter, which also takes image configuration:

    _painter ??= indicator.createBoxPainter(markNeedsPaint);

    final configuration = ImageConfiguration(
    size: _currentRect!.size,
    textDirection: _currentTextDirection,
    devicePixelRatio: devicePixelRatio,
    );
    if (showDivider && dividerHeight! > 0) {
    final dividerPaint = Paint()
    ..color = dividerColor!
    ..strokeWidth = dividerHeight!;
    final dividerP1 = Offset(0, size.height - (dividerPaint.strokeWidth / 2));
    final dividerP2 = Offset(size.width, size.height - (dividerPaint.strokeWidth / 2));
    canvas.drawLine(dividerP1, dividerP2, dividerPaint);
    }
    _painter!.paint(canvas, _currentRect!.topLeft, configuration);

    In this scenario, image is loaded asynchronously, so tracing the flow, we will see what happened inside as below:

    1 . In _indicatorPainter, it requests to create a boxpainter:

    _painter ??= indicator.createBoxPainter(markNeedsPaint);

    1. paint background image in BoxDecorationPainter

    void _paintBackgroundImage(Canvas canvas, Rect rect, ImageConfiguration configuration) {
    if (_decoration.image == null) {
    return;
    }
    _imagePainter ??= _decoration.image!.createPainter(onChanged!);

    1. handle asynchronous image source in DecorationImagePainter

    final listener = ImageStreamListener(_handleImage, onError: _details.onError);

    Inside _handleImage, if it's a non-synchronousCall, _onChanged callback is called:

    if (!synchronousCall) {
    _onChanged();
    }

    _onChanged triggers markNeedsPaint callback, but in markNeedsPaint, it only sets _needsPaint flag to true but doesn't actually trigger a repaint. I did debugging and saw that shouldRepaint is not invoked, even though _needsPaint is true already.

    I have an idea to fix this, it's on the way.

  18. added
    r: fixedIssue is closed as already fixed in a newer version
    on Jan 7, 2026
  19. github-actions commented on Jan 21, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  20. locked as resolved and limited conversation to collaborators on Jan 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P2Important issues not at the top of the work lista: assetsPackaging, accessing, or using assetsa: imagesLoading, displaying, rendering imagesfound in release: 3.3Found to occur in 3.3found in release: 3.7Found to occur in 3.7frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: material_uimaterial_ui package in flutter/packagesr: fixedIssue is closed as already fixed in a newer versionteam-designOwned by Design Languages teamtriaged-designTriaged by Design Languages teamworkaround availableThere is a workaround available to overcome the issue

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions