Search code examples
flutteruser-interfacecross-platformresponsivenessflutter-ui

How to prevent text overflow and enable paragraph-like wrapping in a Flutter Row widget?


I am building a cross-platform application in Flutter, and I am having trouble displaying long text values in a Row layout. Specifically, the text in the InfoRow widget sometimes overflows when it's too long, causing layout issues.

I want the value text to display correctly without overflowing, ideally in a way that it wraps like a paragraph. I have tried using softWrap and overflow but the text is still not displaying as expected.

What I Expect:

I want the value text to wrap and flow within the available space, like a paragraph, without overflow errors. Below is an image of what I want to achieve:

Expected Layout Image

Code:

info_row.dart:
I am using this InfoRow widget to display the label and value pair. The value text is sometimes too long, causing it to overflow.

import 'package:flutter/material.dart';
import 'package:solidcheck_admin/core/constants/app_colors.dart';

class InfoRow extends StatelessWidget {
  final String label;
  final String value;

  const InfoRow({required this.label, required this.value, Key? key})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      scrollDirection: Axis.vertical,
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          SizedBox(
            width: 150.0,
            child: Text(
              label,
              style: const TextStyle(
                fontWeight: FontWeight.w400,
                color: AppColors.textGrey,
                fontSize: 14.0,
              ),
            ),
          ),
          Text(
            value,
            textWidthBasis: TextWidthBasis.longestLine,
            softWrap: true,
            overflow: TextOverflow.ellipsis,
            maxLines: 6,
            style: const TextStyle(
              fontWeight: FontWeight.w400,
              color: AppColors.textBlack,
              fontSize: 14.0,
            ),
          ),
        ],
      ),
    );
  }
}

What Happens:

The text is overflowing in this layout and not wrapping as expected:

Current Layout Image

applicant_header_widget.dart:

This widget is used across multiple screens with different values.

import 'package:flutter/material.dart';
import 'package:solidcheck_admin/data/models/applicant_list.dart';
import 'package:solidcheck_admin/ui/widgets/app/custom_button.dart';
import 'package:solidcheck_admin/ui/widgets/main/tabbar/info_rows.dart';

class ApplicantHeaderDetails extends StatelessWidget {
  final ApplicantListModel applicantModelValue;

  const ApplicantHeaderDetails({
    Key? key,
    required this.applicantModelValue,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final Size size = MediaQuery.of(context).size;

    return Container(
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              _profileInfo(size),
              const SizedBox(height: 30.0),
              Row(
                children: [
                  CustomButton(title: 'Button 1', onPressed: () {}),
                  const SizedBox(width: 8.0),
                  CustomButton(title: 'Button 2', onPressed: () {}),
                  const SizedBox(width: 8.0),
                  CustomButton(title: 'Button 3', onPressed: () {}),
                  const SizedBox(width: 8.0),
                  CustomButton(title: 'Button 4', onPressed: () {}),
                  const SizedBox(width: 8.0),
                  CustomButton(title: 'Button 5', onPressed: () {}),
                ],
              ),
            ],
          ),
          const SizedBox(width: 100.0),
          Container(
            width: size.width * 0.50,
            child: const Wrap(
              spacing: 10.0,
              runSpacing: 10.0,
              children: [
                InfoRow(label: 'Date created:', value: 'Monday, 14-Nov-2022 09:50'),
                InfoRow(label: 'Last updated:', value: 'Friday, 12-July-2023 22:55'),
                InfoRow(label: 'Applicant Status:', value: 'New'),
                InfoRow(label: 'Applicant Status Date:', value: 'Friday, 12-July-2023 22:55'),
                InfoRow(label: 'Consent user:', value: 'Unknown'),
                InfoRow(label: 'Job Title:', value: 'Cleaner'),
                InfoRow(label: 'Check requested:', value: 'Basic Disclosure'),
                InfoRow(label: 'Online status:', value: 'Applicant created by requester'),
                InfoRow(label: 'Requested by:', value: 'Mike Ross\n004407546604213/\n004407546604213\[email protected]'),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _profileInfo(Size size) {
    return Row(
      children: [
        Container(
          height: size.height * 0.18,
          width: size.width * 0.09,
          decoration: BoxDecoration(
            color: Colors.grey[300],
            borderRadius: BorderRadius.circular(8.0),
          ),
          child: const Icon(Icons.image, size: 50.0, color: Colors.grey),
        ),
        const SizedBox(width: 10.0),
        const Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('Thom World', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16.0, color: Colors.black)),
            SizedBox(height: 4.0),
            Text('Send disclosure to client (cannot use eBulk)', style: TextStyle(color: Colors.grey, fontSize: 14.0)),
            SizedBox(height: 8.0),
            Text('Service level 1', style: TextStyle(color: Colors.black, fontWeight: FontWeight.w500, fontSize: 14.0)),
            SizedBox(height: 4.0),
            Text('Primary requester: Test company', style: TextStyle(color: Colors.black, fontWeight: FontWeight.w400, fontSize: 14.0)),
          ],
        ),
      ],
    );
  }
}

Solution

  • wrap your Text widget with Expanded or Flexible widget like this make some other adjustments as well and make it working.

    
    class ApplicantHeaderDetails extends StatelessWidget {
      const ApplicantHeaderDetails({
        Key? key,
      }) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        final Size size = MediaQuery.of(context).size;
    
        return Scaffold(
          body: Container(
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    _profileInfo(size),
                    const SizedBox(height: 30.0),
                    Row(
                      children: [
                        CustomButton(title: 'Button 1', onPressed: () {}),
                        const SizedBox(width: 8.0),
                        CustomButton(title: 'Button 2', onPressed: () {}),
                        const SizedBox(width: 8.0),
                        CustomButton(title: 'Button 3', onPressed: () {}),
                        const SizedBox(width: 8.0),
                        CustomButton(title: 'Button 4', onPressed: () {}),
                        const SizedBox(width: 8.0),
                        CustomButton(title: 'Button 5', onPressed: () {}),
                      ],
                    ),
                  ],
                ),
                const SizedBox(width: 100.0),
                Expanded(
                  // width: size.width * 0.50,
                  child: const Wrap(
                    spacing: 10.0,
                    runSpacing: 10.0,
                    children: [
                      InfoRow(
                          label: 'Date created:',
                          value: 'Monday, 14-Nov-2022 09:50'),
                      InfoRow(
                          label: 'Last updated:',
                          value: 'Friday, 12-July-2023 22:55'),
                      InfoRow(label: 'Applicant Status:', value: 'New'),
                      InfoRow(
                          label: 'Applicant Status Date:',
                          value: 'Friday, 12-July-2023 22:55'),
                      InfoRow(label: 'Consent user:', value: 'Unknown'),
                      InfoRow(label: 'Job Title:', value: 'Cleaner'),
                      InfoRow(label: 'Check requested:', value: 'Basic Disclosure'),
                      InfoRow(
                          label: 'Online status:',
                          value: 'Applicant created by requester'),
                      InfoRow(
                          label: 'Requested by:',
                          value:
                              'Mike Ross\n004407546604213/\n004407546604213\[email protected]'),
                    ],
                  ),
                ),
              ],
            ),
          ),
        );
      }
    
      Widget _profileInfo(Size size) {
        return Row(
          children: [
            Container(
              height: size.height * 0.18,
              width: size.width * 0.09,
              decoration: BoxDecoration(
                color: Colors.grey[300],
                borderRadius: BorderRadius.circular(8.0),
              ),
              child: const Icon(Icons.image, size: 50.0, color: Colors.grey),
            ),
            const SizedBox(width: 10.0),
            const Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text('Thom World',
                    style: TextStyle(
                        fontWeight: FontWeight.bold,
                        fontSize: 16.0,
                        color: Colors.black)),
                SizedBox(height: 4.0),
                Text('Send disclosure to client (cannot use eBulk)',
                    style: TextStyle(color: Colors.grey, fontSize: 14.0)),
                SizedBox(height: 8.0),
                Text('Service level 1',
                    style: TextStyle(
                        color: Colors.black,
                        fontWeight: FontWeight.w500,
                        fontSize: 14.0)),
                SizedBox(height: 4.0),
                Text('Primary requester: Test company',
                    style: TextStyle(
                        color: Colors.black,
                        fontWeight: FontWeight.w400,
                        fontSize: 14.0)),
              ],
            ),
          ],
        );
      }
    }
    
    class InfoRow extends StatelessWidget {
      final String label;
      final String value;
    
      const InfoRow({required this.label, required this.value, Key? key})
          : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            SizedBox(
              width: 150.0,
              child: Text(
                label,
                style: const TextStyle(
                  fontWeight: FontWeight.w400,
                  color: Colors.grey,
                  fontSize: 14.0,
                ),
              ),
            ),
            Flexible(
              child: Text(
                value,
                textWidthBasis: TextWidthBasis.longestLine,
                softWrap: true,
                overflow: TextOverflow.ellipsis,
                maxLines: 6,
                style: const TextStyle(
                  fontWeight: FontWeight.w400,
                  color: Colors.black,
                  fontSize: 14.0,
                ),
              ),
            ),
          ],
        );
      }
    }
    
    

    here is the result

    enter image description here