[Solved] How can I make dynamic text fields

Question

Asked by Shoaib A on January 11, 2022 (source).

I'm working on mathematics app. Currently I'm working on matrices. I want when user select dimension of matrix then on the next screen text fields with same dimension appears.

And how can I handle TextEditing controllers for each text field?

Here is how I'm taking matrix dimension from user

I made a simple text fields to take input from user

  class Matrice extends StatelessWidget {
  Matrice({Key? key}) : super(key: key);

  final controller = Get.find<MatricesController>();

  @override
  Widget build(BuildContext context) {
    return Container(
      // color: Colors.green,
      // height: 50.0,
      width: double.infinity,
      child: Row(
        children: [
          Text(
            '[',
            style: AppTextStyle.kBlackBold
                .copyWith(fontSize: 80.sp, fontWeight: FontWeight.w300),
          ),

          MathsField(
            controller: controller.a11,
          ),

          SizedBox(
            width: 15.0,
          ),
          MathsField(
            controller: controller.a12,
          ),

          Text(
            ']',
            style: AppTextStyle.kBlackBold
                .copyWith(fontSize: 80.sp, fontWeight: FontWeight.w300),
          ),
          Text(
            '+',
            style: AppTextStyle.kBlackBold
                .copyWith(fontSize: 80.sp, fontWeight: FontWeight.w300),
          ),
          Text(
            '[',
            style: AppTextStyle.kBlackBold
                .copyWith(fontSize: 80.sp, fontWeight: FontWeight.w300),
          ),

          MathsField(
            controller: controller.a21,
          ),

          SizedBox(
            width: 15.0,
          ),
          MathsField(
            controller: controller.a22,
          ),

          Text(
            ']',
            style: AppTextStyle.kBlackBold
                .copyWith(fontSize: 80.sp, fontWeight: FontWeight.w300),
          ),
        ],
      ),
    );
  }
}

The output looks like this

And made controller for each textfield

TextEditingController a11 = TextEditingController();
TextEditingController a12 = TextEditingController();
TextEditingController a21 = TextEditingController();
TextEditingController a22 = TextEditingController();

Thanks :)

Answer

Question answered by osaxma (source).

I made a simple example that you can view on DartPad: Matrix Example

Simply, you need to write the logic for creating text fields with controllers for each cell of the Matrix. This will translate into a List<List<TextEditingController>> based on a Matrix custom definition (i.e. has the number of rows and number of columns).

I see in your example that you're creating the controllers elsewhere (outside the widget tree). I'm not sure if this is the approach you would want to take. It's better to manage the Matrix data because that's what you're interested in. Given the data at hand, you can rebuild the matrix as shown below on MatrixPage. This will also allow you to save the data and use it else where (see: printMatrix method on the page, you can do the same to export the data).

The code for reference:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Flutter Demo Home Page'),
        ),
        body: const MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: const <Widget>[
          MatrixOption(matrix: Matrix(1, 1)),
          MatrixOption(matrix: Matrix(2, 2)),
          MatrixOption(matrix: Matrix(3, 3)),
        ],
      ),
    );
  }
}

class Matrix {
  final int rows;
  final int columns;

  const Matrix(this.rows, this.columns);
}

class MatrixOption extends StatelessWidget {
  final Matrix matrix;
  const MatrixOption({
    Key? key,
    required this.matrix,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onTap: () {
        Navigator.of(context).push(
          MaterialPageRoute(
            builder: (context) {
              return Scaffold(
                appBar: AppBar(),
                body: MatrixPage(matrix: matrix),
              );
            },
          ),
        );
      },
      child: Container(
        height: 50,
        width: 100,
        margin: const EdgeInsets.all(8),
        color: Colors.orange,
        child: Center(child: Text('${matrix.rows}' 'x' '${matrix.columns} Matrix ')),
      ),
    );
  }
}

class MatrixPage extends StatefulWidget {
  final Matrix matrix;

  const MatrixPage({
    Key? key,
    required this.matrix,
  }) : super(key: key);

  @override
  State<MatrixPage> createState() => _MatrixPageState();
}

class _MatrixPageState extends State<MatrixPage> {
  // List of lists (outer list is the rows, inner list is the columns)
  final controllers = <List<TextEditingController>>[];
  late final rows = widget.matrix.rows;
  late final columns = widget.matrix.columns;

  @override
  void initState() {
    super.initState();
    createControllers();
  }

  void createControllers() {
    for (var i = 0; i < rows; i++) {
      controllers.add(List.generate(columns, (index) => TextEditingController(text: '0')));
    }
  }

  void printMatrix() {
    final strings = <List<String>>[];
    for (var controllerRow in controllers) {
      final row = controllerRow.map((e) => e.text).toList();
      strings.add(row);
    }
    print(strings);
  }

  @override
  void dispose() {
    for (var controllerRow in controllers) {
      for (final c in controllerRow) {
        c.dispose();
      }
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        children: [
          Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: List.generate(
              controllers.length,
              (index1) => Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: List.generate(
                  controllers[index1].length,
                  (index2) => Center(
                    child: MatrixField(
                      controller: controllers[index1][index2],
                    ),
                  ),
                ),
              ),
            ),
          ),
          TextButton(
            onPressed: printMatrix,
            child: const Text('Print Matrix'),
          )
        ],
      ),
    );
  }
}

class MatrixField extends StatelessWidget {
  final TextEditingController controller;
  const MatrixField({
    Key? key,
    required this.controller,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 50,
      width: 50,
      child: TextField(
        controller: controller,
      ),
    );
  }
}

Let me know if you've any questions about the code.

Video Answers on YouTube

DART FLUTTER FLUTTER-ANIMATION FLUTTER-LAYOUT
SHARE: